刷到leetcode81题搜索旋转排序数组觉得有点意思,遂记录下解题思路和过程。要求实现O(logn)的时间复杂度。
要抓住二分查找的核心,其在于每次排除掉一边的区间,故不在乎整个数组是否是完全递增的,只要每次能排除掉一边的区间,选择另一个区间继续进行二分查找即可。
对于这一题,数组的大小顺序形如下图:
我们以右端点为参照对象进行分类,当中间值==target
时返回true,否则:
- 如果mid值小于右端点时:
如果中间值<target<=右端点
,则下一步对mid右侧进行二分查找,否则对mid左侧进行二分查找 - 如果mid值大于右端点时:
如果左端点<=target<=中间值
,则下一步对mid左侧进行二分查找,否则对mid右侧进行二分查找 - 如果mid值等于右端点时:
直接让右端点向左挪一个单位。(因为target不等于mid值,mid值又等于右端点的值,因此右端点可以被抛弃)
代码如下:
bool search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=(left+right)/2;
//命中则返回true
if(nums[mid]==target)
return true;
//以右端点为参照进行分类
if(nums[mid]<nums[right]){
//就两种情况,要不在左半边,要不在右半边
if(target>nums[mid] && target<=nums[right])
left=mid+1;
else
right=mid-1;
}else if(nums[mid]>nums[right]){
//就两种情况,要不在左半边,要不在右半边
if(target>=nums[left] && target<nums[mid])
right=mid-1;
else
left=mid+1;
}else{
//抛弃右端点
right--;
}
}
return false;
}
这里再看一道旋转数组的变形,同样要求时间复杂度O(logn):
这里同样应用刚才说的二分查找的核心思路,每次排除掉一边,对另一边继续进行二分查找。代码如下:
class Solution {
public:
int findMin(vector<int>& nums) {
if(nums.size()==1)
return nums[0];
int left=0,right=nums.size()-1;
while(left<right-1){ //缩小到最小区间
int mid=(left+right)/2;
if(nums[mid]<nums[right]){
//最小值只有可能在左侧
right=mid;
}else if(nums[mid]>nums[right]){
//最小值只可能在右边
left=mid;
}else
//右端点此时不可能是最小值,排除
right--;
}
return min(nums[left],nums[right]); //返回最后两个候选人中较小的
}
};