对于有序数组进行查找我们可以用二分查找,可以参考我之前写的 Search Insert Position
但如果把这个排序的数组当成一个环,然后做一个旋转操作后再截断,再进行查找又会是什么情况呢?且看下文分解。
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e.,
0 1 2 4 5 6 7
might become4 5 6 7 0 1 2
).You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
分成了两部分,而且是分别排序的,我们是不是也可以用二分呢?怎么用?先看看以下分析:
如果二分查找中的mid是落在左侧,则有A[mid] > A[begin] > A[end-1],否则有A[mid] < A[end - 1] < A[begin]
在我们确定是在哪一侧之后,那问题就变得简单了。
在左侧时,只需要比较target与A[begin];在右侧时,只需要比较target与A[end]。
具体代码吧:
1 class Solution { 2 public: 3 int search(int A[], int n, int target) { 4 int begin = 0, end = n; 5 int mid; 6 while (begin < end) { 7 mid = begin + ((end - begin) >> 1); 8 if (A[mid] == target) 9 return mid; 10 if (A[mid] > A[begin]) { 11 if (A[mid] < target) { 12 begin = mid + 1; 13 } else { 14 if (A[begin] < target) 15 end = mid; 16 else if (A[begin] > target) 17 begin = mid + 1; 18 else 19 return begin; 20 } 21 } else { 22 if (A[mid] > target) { 23 end = mid; 24 } else { 25 if (A[end - 1] < target) 26 end = mid; 27 else if (A[end-1] > target) 28 begin = mid + 1; 29 else 30 return end - 1; 31 } 32 } 33 } 34 return -1; 35 } 36 };
对于这道题算是完美解决了,细心的同学们可能会发现这题的说明里面特意提到no duplicate,那如果有duplicate又怎样了?于是有了这题的变型:
Follow up for "Search in Rotated Sorted Array":
What if duplicates are allowed?Would this affect the run-time complexity? How and why?
Write a function to determine if a given target is in the array.
有重复元素又会怎样?我们上面解法的关键在于我们能区分得出mid点是在左侧还是右侧,那如果存在重复的元素呢?且看下图
当mid上的点与begin与end-1上的点都相等时,你无法判断是左侧还是右侧!
那怎么解决这个问题呢?我们想想,合并重复元素不就回到之前的问题了?
不过这里我们不用遍历一遍数组然后把重复的元素合并,一方面这样会破坏数组,另一方面,我们还有更高明的办法。
利用好排序的特点,我们只需要begin重复的往后移,end重复的往前移即可。且看代码:
1 class Solution { 2 public: 3 bool search(int A[], int n, int target) { 4 int begin = 0, end = n, mid; 5 while (begin < end) { 6 // 检测首尾重复的并移动 7 while (begin < end - 1 && A[begin] == A[begin + 1]) 8 ++begin; 9 while (end > begin + 1 && A[end - 1] == A[end - 2]) 10 --end; 11 mid = begin + ((end - begin) >> 1); 12 if (A[mid] == target) 13 return true; 14 if (A[mid] > A[begin]) { 15 if (A[mid] < target) { 16 begin = mid + 1; 17 } else { 18 if (A[begin] > target) { 19 begin = mid + 1; 20 } else if (A[begin] < target) { 21 end = mid; 22 } else { 23 return true; 24 } 25 } 26 } else { 27 if (A[mid] > target) { 28 end = mid; 29 } else { 30 if (A[end - 1] > target) { 31 begin = mid + 1; 32 } else if (A[end - 1] < target) { 33 end = mid; 34 } else { 35 return true; 36 } 37 } 38 } 39 } 40 return false; 41 } 42 };