1. 思路
通过二分查找找到翻转点,然后再在两边进行二分查找。
2.代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int l, r, point = -1, size = nums.size() - 1;
l = 0, r = size;
while(l <= r)
{
int mid = (l+r) >> 1;
if(mid < size && nums[mid+1] < nums[mid])
{
point = mid;break;
}
if(nums[mid] >= nums[0]) l = mid + 1;
else if(nums[mid] <= nums[size]) r = mid - 1;
}
int ret = 0;
if(point != -1)
{
ret = search(nums, target, 0, point);
if(ret >= 0) return ret;
ret = search(nums, target, point + 1, size);
if(ret >= 0) return ret;
}
else{
ret = search(nums, target, 0, size);
if(ret >= 0) return ret;
}
return -1;
}
int search(vector<int>& nums, int target, int left, int right)
{
while(left <= right)
{
int mid = (left+right) >> 1;
if(nums[mid] < target) left = mid + 1;
else if(nums[mid] > target) right = mid - 1;
else return mid;
}
return -1;
}
};
3. 大神思路
其实之前写二分查找一直都很玄学,因为二分查找思路很简单,框架很容易记。但是其中的细节就不是去理解。一般都是每次要写的时候,发现结果不对,看下bad-case也就能改对。
所以对二分查找理解不透彻的建议先看下这个文章: 二分查找细节理解
然后再来看下大神思路。
对于一般的二分查找来说,待搜索序列是单调的,所以我们能够根据单调性来将搜索区间向左压缩,或者向右压缩。
那么这个题目序列不是单调的,那么能够通过一次二分查找找到目标元素吗?
其实是可以的,因为二分查找的真正核心要求有序不是简单的比大小,而是在某个规则下有序:
对于任意一个元素,能够在O(1)的时间复杂度得到,目标元素在当前元素的左边还是右边。
那么对于这个题目,我们如何来分析呢:
首先,我们知道原数组可以分为前后两部分:
4 , 5, 6 | 1, 2, 3 |
---|
这两部分都有规律:
- 两部分都是分别单调递增的。
- 后面部分比前面一部分每个数小。
那么根据这个规律的话,我们可以重新给这个序列定一个比较关系。
- 当都在同一个部分的时候,根据数字大小比较。
- 如果不在同一个部分的时候,数字大的反而小。
那么在新的比较关系下,那么这个序列就变成有序的了!
4. 大神代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right)
{
int mid = (left + right) >> 1;
if((target < nums[0]) ^ (nums[mid] < nums[0]) ^ (nums[mid] <= target))
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return right >= 0 && nums[right] == target ? right : -1;
}
};