LeetCode 33.搜索旋转排序数组

解法一:顺序查找

时间复杂度:O(n)

int search(vector<int>& nums, int target) {
    for(int i=0;i<nums.size();i++){
        if(nums[i]==target) return i;
    }
    return -1;
}

解法二:二分查找

分析:

题目进阶中,要求设计一个O(logn)的方案,根据时间复杂度,在查找方案中很容易想到二分查找。

但与正常的二分查找不同,该数组是在某个下标 k 下进行了旋转,因此,在二分查找时,需要做一些特定的处理。

假设数组旋转之前为:【0 1 2 4 5 6 7】

旋转之后可能是(k=3):【4 5 6 7 0 1 2】

普通的二分查找:

前提:有序序列

将待查找序列根据中间元素 mid ,将序列划分为 [low,mid-1]mid[mid+1,high]

  • 如果待查找元素 target==nums[mid],代表查找成功,返回 mid
  • 如果 target < nums[mid],则在左半部分 [low,mid-1]继续二分查找
  • 如果 target > nums[mid],则在右半部分 [mid+1,high]继续二分查找

直到找到与target相等元素的位置,或发现序列中不存在与target相等的值

时间复杂度:O(logn)

普通二分查找的代码:

//普通二分 
int binarySearch(vector<int>& nums, int target){
    if(nums.size()==0) return -1;
    int low=0,high=nums.size()-1;
    while(low<=high){
        int mid = low + (high-low)/2; //避免low+high越界 
        if(nums[mid]==target) return mid;
        else if(nums[mid]<target) low=mid+1;
        else high=mid-1;
    } 
    //结束条件:low>high 
    return -1;
}

经过旋转后的数组,只是局部有序,整体并不有序,那么本题如何应用二分?

与普通的二分查找一样,根据中间元素 mid ,将序列划分为 [low,mid-1]mid[mid+1,high]

  1. 如果target == nums[mid] ,代表找到了该元素,直接返回即可;
  2. 否则,比较target 大于还是小于 nums[mid] ,决定下一个查找的子序列

与普通的二分不同的是,在比较targetnums[mid] 谁大谁小之前,先比较 nums[low]nums[mid] 的关系,原理如下:

第一类序列:【2 3 4 5 6 7 1】,也就是 nums[low] < nums[mid] ,此例中为2<5。这种情况下,可以保证序列的前半部分是有序的。

  • 如果 nums[low] <= target < nums[mid] ,则去前半部分找
  • 否则,去后半部分找

第二类序列:【6 7 1 2 3 4 5】,也就是 nums[low] > nums[mid] ,此例中为6>2。这种情况下,可以保证序列的后半部分是有序的。

  • 如果 nums[mid] <= target < nums[end] ,则去后半部分找
  • 否则,去前半部分找

根据比较 nums[low]nums[mid] 的大小,得出哪一个子序列是有序的,因为只有有序,才可以进行下一步的大小比较,如判断 nums[low] <= target < nums[mid] 是否成立,如果不能确定具体的序列是否有序,就开始比较的话,很可能发生错误。如序列【3 4 5 6 7 1 2】,就永远找不到一个大于7小于2的元素。

代码:

int rotateBinarySearch(vector<int>& nums, int target){
    if(nums.size()==0) return -1;
    int low=0,high=nums.size()-1;
    while(low<=high){
        int mid = low+(high-low)/2;
        if(nums[mid]==target) return mid;
        else if(nums[low]<=nums[mid]){  //下面两个if中 target一定不等于 nums[mid],因为是从上面的if过来的 
            if(nums[low]<=target && target<nums[mid]){ //mid's left 
                high=mid-1;
            }else { //mid's right
                low=mid+1;
            }
        }else { //nums[low]>nums[mid]
            if(nums[mid]<target && target<=nums[high]){ //right
                low=mid+1;
            }else { //left
                high = mid-1;
            }
        }
    } 

    return -1;
} 
posted @ 2021-05-18 21:41  KyrieLiu  阅读(267)  评论(0编辑  收藏  举报