仅供自己学习
思路:
思路比较简单,但要注意细节处理。
一开始就是想遍历寻找nums[i]<nums[i-1]获得旋转点,然后对这两侧的数组分别使用二分搜索,但是一直报错,找不到原因。
根据题解二分可知二分的本质是二段性,而非单调性。只要一段满足某个性质,另外一段不满足这个性质就可以用二分。
对于一个递增的数组[1,2,3,4,5,6,7],旋转后[4,5,6,7,1,2,3],前半段满足>=nums[0],但后半段不满足>=nums[0],满足二段性,就可以使用二分找到旋转点。
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (nums[mid] >= nums[0]) {
l = mid;
} else {
r = mid - 1;
}
}
通过if可以找到最后一个大于等于nums[0]的元素。
但如果[0,1,2,2,2,3,4,5]旋转为[2,3,4,5,0,1,2,2]旋转点为第二个2,那么0元素前满足>=nums[0],而0元素后面的2的满足了等于,所以失去了二段性,不能用二分。我们需要通过回复二段性来让这段数组能使用二分。
while(left<right&&nums[0]==nums[right])right--;
通过该代码回复二段性
然后就可以用二分法找到旋转点,因为上述找旋转点的代码是找到最后一个大于nums[0]的元素,所以还要加1才能得到原数组开始的元素。
然后再对这两段调用二分搜索即可,调用的二分也是找到第一个一个大于等于target的二分搜索,如果等于就返回target位置。
代码:
class Solution {
public:
bool search(vector<int>& nums, int target) {
int n=nums.size();
if(n==0) return false;
if(n==1) return nums[0]==target;
int left=0,right=n-1;
int end=n;
while(left<right&&nums[0]==nums[right])right--;
while(left<right){
int mid =(left+right+1)>>1;
if(nums[mid]>=nums[0]) {
left=mid;
}
else{
right=mid-1;
}
}
if(nums[right]>=nums[0]&&right+1<n) end=right+1;
int res = binary_search(nums,target,0,end-1);
if(res!=-1) return true;
res = binary_search(nums,target,end,n-1);
return res!=-1;
}
int binary_search(vector<int>& nums,int target,int start,int end){
int left=start,right=end;
while(left<right){
int mid=(left+right)/2;
if(nums[mid]>=target) right=mid;
else left=mid+1;
}
return nums[right]==target?right:-1;
}
};