|
| 1 | +# 题目描述(中等难度) |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +[33题](<https://leetcode.wang/leetCode-33-Search-in-Rotated-Sorted-Array.html>)的升级版,数组的操作没有变,所谓的旋转数组,就是把有序数组前边若干个数字移动到末尾。区别在于这道题出现了重复的数字,同样是找 target。 |
| 6 | + |
| 7 | +# 解法一 |
| 8 | + |
| 9 | +把数组遍历一遍,然后依次判断数字是否相等的解法,当然就不用说了。这里直接在[33 题](<https://leetcode.wang/leetCode-33-Search-in-Rotated-Sorted-Array.html>)解法三的基础上去修改。33 题算法基于一个事实,数组从任意位置劈开后,至少有一半是有序的,什么意思呢? |
| 10 | + |
| 11 | +比如 [ 4 5 6 7 1 2 3] ,从 7 劈开,左边是 [ 4 5 6 7] 右边是 [ 7 1 2 3],左边是有序的。 |
| 12 | + |
| 13 | +基于这个事实。 |
| 14 | + |
| 15 | +我们可以先找到哪一段是有序的 (只要判断端点即可),知道了哪一段有序,我们只需要用正常的二分法就够了,只需要看 target 在不在这一段里,如果在,那么就把另一半丢弃。如果不在,那么就把这一段丢弃。 |
| 16 | + |
| 17 | +```java |
| 18 | +public int search(int[] nums, int target) { |
| 19 | + int start = 0; |
| 20 | + int end = nums.length - 1; |
| 21 | + while (start <= end) { |
| 22 | + int mid = (start + end) / 2; |
| 23 | + if (target == nums[mid]) { |
| 24 | + return mid; |
| 25 | + } |
| 26 | + //左半段是有序的 |
| 27 | + if (nums[start] <= nums[mid]) { |
| 28 | + //target 在这段里 |
| 29 | + if (target >= nums[start] && target < nums[mid]) { |
| 30 | + end = mid - 1; |
| 31 | + //target 在另一段里 |
| 32 | + } else { |
| 33 | + start = mid + 1; |
| 34 | + } |
| 35 | + //右半段是有序的 |
| 36 | + } else { |
| 37 | + if (target > nums[mid] && target <= nums[end]) { |
| 38 | + start = mid + 1; |
| 39 | + } else { |
| 40 | + end = mid - 1; |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + } |
| 45 | + return -1; |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +如果不加修改,直接放到 leetcode 上跑,发现 nums = [ 1, 3, 1, 1, 1 ] ,target = 3,返回了 false,当然是不对的了。原因就出现在了,我们在判断哪段有序的时候,当 nums [ start ] <= nums [ mid ] 是认为左半段有序。而由于这道题出现了重复数字,此时的 nums [ start ] = 1, nums [ mid ] = 1,但此时左半段 [ 1, 3, 1 ] 并不是有序的,所以造成我们的算法错误。 |
| 50 | + |
| 51 | +所以 nums[start] == nums[mid] 需要我们单独考虑了。操作也很简单,参考[这里](<https://leetcode.com/problems/search-in-rotated-sorted-array-ii/discuss/28218/My-8ms-C%2B%2B-solution-(o(logn)-on-average-o(n)-worst-case),当相等的时候,我们只需要让 start++ 就够了。 |
| 52 | + |
| 53 | +```java |
| 54 | +public boolean search(int[] nums, int target) { |
| 55 | + int start = 0; |
| 56 | + int end = nums.length - 1; |
| 57 | + while (start <= end) { |
| 58 | + int mid = (start + end) / 2; |
| 59 | + if (target == nums[mid]) { |
| 60 | + return true; |
| 61 | + } |
| 62 | + //左半段有序 |
| 63 | + if (nums[start] < nums[mid]) { |
| 64 | + if (target >= nums[start] && target < nums[mid]) { |
| 65 | + end = mid - 1; |
| 66 | + } else { |
| 67 | + start = mid + 1; |
| 68 | + } |
| 69 | + } else if(nums[start] == nums[mid]){ |
| 70 | + start++; |
| 71 | + //右半段有序 |
| 72 | + }else{ |
| 73 | + if (target > nums[mid] && target <= nums[end]) { |
| 74 | + start = mid + 1; |
| 75 | + } else { |
| 76 | + end = mid - 1; |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + return false; |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +时间复杂度:最好的情况,如果没有遇到 nums [ start ] == nums [ mid ],还是 O(log(n))。当然最差的情况,如果是类似于这种 [ 1, 1, 1, 1, 2, 1 ] ,target = 2,就是 O ( n ) 了。 |
| 85 | + |
| 86 | +空间复杂度:O ( 1 )。 |
| 87 | + |
| 88 | +# 总 |
| 89 | + |
| 90 | +基于之前的算法,找出问题所在,然后思考解决方案。开始自己一直纠结于怎么保持时间复杂度还是 log ( n ),也没想出解决方案,看了 discuss,发现似乎只能权衡一下。另外 [33题](<https://leetcode.wang/leetCode-33-Search-in-Rotated-Sorted-Array.html>) 的另外两种解法,好像对于这道题完全失效了,如果大家发现怎么修改,欢迎和我交流。 |
0 commit comments