基本思想:
一定要注意一下这道题,和“154. 寻找旋转排序数组中的最小值 II”思路类似,但是解决思路不同会有问题;
首先确定的是,肯定是用二分,但是一定要使其变为二段性;
何为二段性,就是指在二分的时候,一定要一半满足一个条件,另一半不满足,才能进行二分;
例如:
1,1,1,3,1
和1,1,1,1,3
一个可以二分,一个就不可以;
原因是第一个序列无法通过某个索引使其分割为1,1,1
,3
这两个序列;
154题给的题解是在遍历中直接忽略和mid相等的右边界;
而宫水三叶大佬则是:
这里注意一个重要问题:
看似两种方法都能解决,但是有本质区别;
官方题解只能选找到最小元素,但是无法寻找到拐点,也就是说,他找到的最小元素未必是真正的拐点;
例如:1,1,1,1,3,1;
按照官方题解的思路会将其转化为:
1,1,1,1,3;
此时,对比右边界,会找到第一个1;而其实真正拐点的1应该是3后面的1;
而按照宫水三叶的思路,也会将其转化为:
1,1,1,1,3;
但是由于对比的是左边界,寻找最大值后便可以找到第一位最小值;
因此会找到3,得到最小值应该是3后面的1;
同理,如果换个例子:
3,3,3,3,1,3;
转化为:
3,3,3,3,1;
则会找到最后一个3,可以找到最小值1;
因此,可以看到宫水三叶的思路是找到最大值,然后+1找到最小值即可;
所以此题可以转化为,找到拐点,分两边进行二分寻找;
具体代码:
class Solution {
public:
int search(vector<int>& arr, int target) {
int ret;
int l=0,r=arr.size()-1;
while(r>=0&&arr[0]==arr[r]){
r--;
}
//先找到最小元素边界;
while(l<r){
int mid=(l+r+1)>>1;
if(arr[0]<=arr[mid]){
l=mid;
}else{
r=mid-1;
}
}
//分为两半进行遍历;
if(target>=arr[0]){
ret=search(0,l, target, arr);
}else{
ret=search(l+1, arr.size()-1, target, arr);
}
if(target==arr[ret])
return ret;
return -1;
}
int search(int left,int right,int target,vector<int>& arr){
int l=left,r=right;
while(l<r){
int mid=l+(r-l)/2;
if(arr[mid]>=target){
r=mid;
}else{
l=mid+1;
}
}
return l;
}
};