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;
}