leetcode上遇到了一个问题,就是如果把一个sorted数组旋转一下,如[0,1,2,3,4,5,6,7] => [4,5,6,7,0,1,2,3],这样二分查找还将如何进行?,当然是用O(log(n))的方法。
问题定义
应该是如下的场景,
1 2
| int search(vector<int> nums, int target) { }
|
不重复的情况
这里会有一个问题场景的问题,即数组中是否允许存在重复的元素? 如果不允许,则找到旋转的那个点,如例子中是在value=0处,即idx=4,找到了这个点的方法有些多,可以使用寻找最小值的方法,也可以直接二分查找,如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| int findMinIdx(vector<int> nums) { int lo = 0, hi = nums.size() - 1; while(lo < hi) { int mid = lo + (hi - lo) / 2; if(nums[mid] > nums[hi]) { lo = mid + 1; } else { hi = mid - 1; } } // lo==hi is the index of the smallest value and also the number of places rotated. }
|
也可以直接二分的算法查找,只不过原来的方法要稍微变动下,这种方法比较高效,不够思路有些不太清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int search(vector<int> nums, int target) { int start = 0, end = nums.end() - 1; while(start < end) { int mid = start + (end - start) / 2; if(nums[mid] == target) { return mid; } else if(nums[mid] > nums[start]) { if(target < nums[mid] && target >= nums[start]) { end = mid - 1; } else { start = mid + 1; } } else if(nums[mid] < nums[start]){ if(target >= nums[start] || target < nums[mid]) { end = mid - 1; } else { start = mid + 1; } } else { start++; } } return -1; }
|
重复场景如何?
这个没有争议,关键点在于如果数组中允许存在相同的元素,这个算法该怎么写,可以肯定的是上面的算法都将失效,比如[4,4,4,4,0,1,2,4]找不到最小的地方的,还有[4,6,4,4,4,4,4,4]如何找到rotated place是2,很难,经过验证,无法在log(n)的时间内找到翻转的位置,不过可以找到最小的值是多少,至于在哪个位置就不确定了,即最多找出一个最小值的idx。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| int findMinValue(vector<int> nums, int start, int end) { if(start > end) { return -1; } int mid = start + (end - start) / 2; while(start < end) { if(nums[mid] > nums[start]) { int pos = findMinValue(nums, mid + 1, end); if(pos == -1) { return start; } return nums[start] < nums[pos] ? start : pos; } else if(nums[mid] < nums[start]) { int pos = findMinValue(nums, start, mid - 1); if(pos == -1) { return mid; } return nums[pos] < nums[mid] ? pos : mid; } else { //相等的情况较为复杂, 两边都要考虑 int pos = mid; int pos1 = findMinValue(nums, start, mid - 1); if(pos1 != -1) { pos = nums[pos] < nums[pos1] ? pos : pos1; } int pos2 = findMinValue(nums, mid + 1, end); if(pos2 != -1) { pos = nums[pos] < nums[pos2] ? pos :pos2; } return pos; } } return -1; }
|