【刷题笔记】三种方法通关leetcode33.旋转排序数组

10 篇文章 0 订阅
5 篇文章 0 订阅

写在前面

前段时间虽说一直保持刷题,但是由于课业繁重,经过组织上的反复权衡,决定停更一段时间。
现在正好是课业的空窗期,于是又来水博客力。

看官们觉得不错可以赏脸点个关注再走哦~

我的 github

二分查找的使用条件

二分查找的 O ( l o g n ) O(logn) O(logn) 高效必然有局限性:

这里就是前提条件
1. 有序序列
2. 存在上下界
3. 随机访问特性

代码模板
left, right = 0, len(array) - 1
while left <= right:
  mid = (left+right)/2
  if array[mid] == target:
    #find the target
    break or return result
  elif array[mid] < target:
    left = mid + 1
  else: 
    right = mid - 1
搜索旋转排序数组
  1. 暴力还原

我们用 O ( n ) O(n) O(n) 的复杂度进行peak的搜索,还原为有序,然后二分查找

  1. 二分查找还原

O ( l o g n ) O(logn) O(logn)的复杂度搜索peak(二分查找),然后还原为有序,再进行二分查找

class Solution {
public:
  //主函数
    int search(vector<int>& nums, int target) {

        if(nums.size() == 0) return -1;
        
        int peak  = peakIndexInMountainArray(nums);
        
        if(nums[peak] < target) {
           return bs(nums, 0, peak, target);
        }
       
        return bs(nums, peak+1, nums.size(), target);
        
        

    }
  // 这边偷懒一下 用一下 leetcode 852.的代码
    int peakIndexInMountainArray(vector<int>& nums) {
        // bs 
         if(nums.size()==1) return nums[0];
        long long l= 0,  r = nums.size() -1;

       
        while(l<=r) {
            int mid = (l + r )/2;

            if(nums[mid] < nums[mid +1]) l = mid +1;
            else if(nums[mid] < nums[mid - 1]) r = mid - 1;
            else return mid;
        }
        return -1; 
    }
    int bs(vector<int> &nums, int low, int high, int target) {
        
        while(low <= high) {
            
            int mid = (low + high ) >> 1;

            if(nums[mid] < target) low = mid+1;
            else if(nums[mid] > target) high = mid -1 ; 
            // 找到重复元素的第一个元素
            else {
                if(mid==0|| nums[mid-1] != target) return mid;
                high = mid - 1;
            }
            
        }

        return -1 ;
    }
};
  1. 正解:改造的二分查找

由于题目给出的输入是局部单调的,在某两段存在一定的单调性。

二分查找的条件:

  1. 单调(非严格满足)
  2. 边界(满足)
  3. 随机访问(满足)

例如:

nums = [4,5,6,7,0,1,2] , target = 0

原味二分查找中,收敛条件大于或者小于 mid 值

因此,这里我们需要思考的是,target将会在哪一段单调部分,然后把所有区间罗列出来

我们依然取

mid = left + (right - left) / 2

然后,我们比较三个变量

nums[0]  	nums[mid] 	 target

接下来,就罗列一下所有情况:(下面的i可以看作 high 位)

  1. nums[0] <= nums[mid],说明0 - mid有序。

    1. nums[0] <= target < nums[mid],我们只要在 0 - mid 范围内查找,那也就是按照原味二分查找进行收敛。
  2. nums[0] > nums[mid],说明0 - mid存在peak。

    1. nums[0] > nums[mid] >= target,此时target在peak 后方,我们向后收敛

    2. target >= nums[0] > nums[mid],此时向前收敛

    3. nums[0] > target >= nums[mid],此时向后收敛

    所有情况的图解

这里peak这个变量我们就不考虑了

如此,我们就不难写出代码:(虽说“不难”,其实磨了好久的细节,感觉离散数学还给老师了)

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size()==0) return -1;

        int l = 0, h = nums.size() - 1;
        // 按照两种情况二分
        while(l < h) {
            int mid = l + ((h-l) >> 1);

            if(nums[mid] == target) return mid;
           // 0 - mid 有序 
            if( nums[l] <= nums[mid]) {
                // target 在这个范围内
                if( target >= nums[l] && target < nums[mid] ) h = mid - 1; 
                else  l = mid + 1;
            }
  
           // 0 - mid 存在peak
           // nums[l] > nums[mid]
            else {
              // target 在后半段有序序列
                if(target > nums[mid] && target <= nums[h] ) l = mid + 1;
                else  h = mid - 1;
            }

        }
        return nums[l] == target ? l : -1;
    }
  
};

最后顺便给一种简洁版解法,要理解的这个解法的话,要点在于自己画一张真值表

/*
作者:LukeLee
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/ji-jian-solution-by-lukelee/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int lo = 0, hi = nums.size() - 1;
        while (lo < hi) {
            int mid = (lo + hi) / 2;
            if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]))
                lo = mid + 1;
            else
                hi = mid;
        }
        return lo == hi && nums[lo] == target ? lo : -1;
    }
};

我们结合真值表,结合前面的解法,不难证明这种方案的正确性。(但是能想出来是真的大神)
证明以上解法的正确性

Ref

极简solution

Revised Binary Search

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值