LeetCode二分查找题目总结

二分查找,通过不断二分缩小查找范围。

二分查找并不简单,KMP发明人之一Knuth都说二分查找:思路很简单,细节是魔鬼。二分查找里真正的坑不是整型溢出的bug,而是在于到底要给 mid 加一还是 减一,while 里到底用 <= 还是 <。

if的情况能详细尽量详细.

划分 [left, mid] 与 [mid + 1, right] ,mid 被分到左边,对应 int mid = left + (right - left) / 2;;
划分 [left, mid - 1] 与 [mid, right] ,mid 被分到右边,对应 int mid = left + (right - left + 1) / 2;。
至于为什么划分是这种对应关系,原因在于区间只有 22 个数的时候,如果中间数的取法不对,一旦进入的分支不能使得区间缩小,会出现 死循环。暂时不理解问题不大,需要在练习中进行调试;



 

注意:向一个方向搜索的时候,mid注意向上取整还是向下取整.

153、154 题:搜索最小值

目录

1、寻找旋转排序数组中的最小值 (题目)

(如果中值 < 右值,则最小值在左半边,可以收缩右边界 right=mid。
如果中值 > 右值,则最小值在右半边,可以收缩左边界 left=mid+1。

 


0.基本模板

int binarySearch(int[] nums, int target) {
    int left = 0, right = ...;
 
    while(...) {
        int mid = (right + left) / 2;
        if (nums[mid] == target) {
            ...
        } else if (nums[mid] < target) {
            left = ...
        } else if (nums[mid] > target) {
            right = ...
        }
    }
    return ...;
}

 

 

1、寻找旋转排序数组中的最小值 (题目

 

单调递增的序列:

        *
      *
    *
  *
*
做了旋转:

  *
*
        *
      *
    *
用二分法查找,需要始终将目标值(这里是最小值)套住,并不断收缩左边界或右边界。

左、中、右三个位置的值相比较,有以下几种情况:

左值 < 中值, 中值 < 右值 :没有旋转,最小值在最左边,可以收缩右边界 (right =mid)

        右
     中
 左
左值 > 中值, 中值 < 右值 :有旋转,最小值在左半边,可以收缩右边界 (right =mid)

 左       
         右
     中
左值 < 中值, 中值 > 右值 :有旋转,最小值在右半边,可以收缩左边界 (left肯定不是mid,left = mid+1)

     中  
 左 
         右
左值 > 中值, 中值 > 右值 :单调递减,不可能出现

 左
    中
        右
分析前面三种可能的情况,会发现情况1、2是一类,情况3是另一类。

如果中值 < 右值,则最小值在左半边,可以收缩右边界。
如果中值 > 右值,则最小值在右半边,可以收缩左边界。

通过比较中值与右值,可以确定最小值的位置范围,从而决定边界收缩的方向。

而情况1与情况3都是左值 < 中值,但是最小值位置范围却不同,这说明,如果只比较左值与中值,不能确定最小值的位置范围。

所以我们需要通过比较中值与右值来确定最小值的位置范围,进而确定边界收缩的方向。主要通过向右收缩边界。

想一下模板:

class Solution {
    public int findMin(int[] nums) {
        int left =0,right = nums.length-1;
        while (left<= right){
            int mid = left +(right-left)/2;
            if (nums[mid] >= nums[right])
                left = mid+1;
            else
                right =mid;
        }
        return nums[right];
    }
}

或者这个更容易理解:

class Solution {
    public int findMin(int[] nums) {
        int left =0,right = nums.length-1;
        while (left<right){  //查找到的时候,刚好左右相等在对应位置上,
            int mid = left +(right-left)/2;
            if (nums[mid] <= nums[right])
                right =mid;
            else
                left = mid+1;

        }
        return nums[left];
    }
}

再讨论一个问题:

为什么左右不对称?为什么比较mid与right而不比较mid与left?能不能通过比较mid与left来解决问题?

左右不对称的原因是:
这是循环前升序排列的数,左边的数小,右边的数大,而且我们要找的是最小值,肯定是偏向左找,所以左右不对称了。

为什么比较mid与right而不比较mid与left?
具体原因前面已经分析过了,简单讲就是因为我们找最小值,要偏向左找,目标值右边的情况会比较简单,容易区分,所以比较mid与right而不比较mid与left。

那么能不能通过比较mid与left来解决问题?
能,转换思路,不直接找最小值,而是先找最大值,最大值偏右,可以通过比较mid与left来找到最大值,最大值向右移动一位就是最小值了(需要考虑最大值在最右边的情况,右移一位后对数组长度取余)。 

先求旋转数组的最大值,再加一(又学会一道题目【求旋转数组的最大值】)

道理一样,如果 左<中<右,则最大值肯定在右边,不包含mid,left=mid;(其实是mid+1,但mid肯定行,为了保持和后边一致)                      如果 左<中>右,则最大值肯定在右边,包含mid  .left =mid;
                 如果 左>中<右,则最大值肯定在左边,right =mid -1;  (注意这里向左收缩边界,注意二分的时候,向上取整而不是向下取整。假设数组1,3,5,7 left=mid 的时候,mid-1会导致右边的7被淘汰)

class Solution {
    public int findMin(int[] nums) {
        int left =0,right = nums.length-1;
        while (left<= right){
            int mid = left +(right-left+1)/2;  // 注意区分
            if (nums[mid] <= nums[left])
                right = mid -1;
            else
                left =mid ;

        }
        return nums[(left + 1) % nums.length];
    }
}

 

 

2、154. 寻找旋转排序数组中的最小值 II

class Solution {
    public int findMin(int[] nums) {
        int left =0,right = nums.length-1;
        while (left<=right){
            int mid = left +(right-left)/2;
            if (nums[mid] == nums[right])
                right -=1 ; // 想想为什么这里不是left+=1?看看比较的对象
            else if (nums[mid] > nums[right])
                left = mid+1;
            else
                right =mid;
        }
        return nums[left];
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值