二分查找法学习心得(如何具体问题具体分析)

题目:

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

思路:

  • 做此题时,偶然读到了一篇非常好的关于二分查找法的博客,令我醍醐灌顶,明白了自己之前看二分法是如何的僵硬固化。
  1. 对于两种循环条件的选择
    • 有一些问题可以在循环之中找到答案,所以写法是 while(left <= right) ,循环体内是三个分支。更多的问题需要等到退出循环以后得到答案,所以写法是 while(left < right) ,循环体内是两个分支。
    • 若left = right进入循环体中但却找不到答案,left和right会处于交错的状态,不利于我们思考,因此选用 < 表示最后退出循环是left = right时,此时对left或right进行操作都可以。
  2. 要明确下一轮搜索区间是什么,进而设置左右边界,二分查找只有一个思想,那就是:逐步缩小搜索区间
  3. 两个易错点
    • 取 mid 的时候,有些时候需要 +1,这是因为需要避免死循环;这是因为 int mid = (left + right) / 2 在区间里有偶数个元素的时候,mid 只能取到位于左边的中间数,要想取到位于右边的中间数,就需要在括号里加 1。
    • 什么时候 right 取 len ?什么时候 right = len - 1。看题目,每个问题的答案都未必一样。
  4. 读题时,要确定元素找寻的条件,才能确定边界,不可套模板。要只把区间分成两个部分,这是因为:只有这样,退出循环的时候才有 left 与 right 重合,我们才敢说,找到了问题的答案。
  5. while (left < right) 不表示搜索区间为「左闭右开」,也不表示搜索区间为「左闭右闭」, 它们没有因果关系,while (left < right) 只表示它本来的意思:循环可以继续的条件是 left < right 。边界如何设置,这一点完全是人为定义的。
  6. 二分查找法在于我们能够根据题意:得到某种单调性,和可以逐步缩小搜索规模的条件,进而准确地设计可以使得搜索区间缩小的条件
  7. 退出循环 left == right,如果可以确定区间 [left..right] 一定有解,直接返回 left 就可以,否则还需要对 left 这个位置单独做一次判断;始终保持不变的是:在区间 [left..right] 里查找目标元素。

想法:

对于此题

题目要找的元素是:第一个大于等于 target 的元素的下标;
数组的长度 len 也有可能是问题的答案,设置 right = len 不是因为设置区间是「左闭右开」,而是因为 len 本来就有可能是问题的答案。

int len = nums.length;
        int left = 0;
        int right = len;
        // 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target){
                // 下一轮搜索的区间是 [mid + 1..right]
                left = mid + 1;
            } else {
                // 下一轮搜索的区间是 [left..mid]
                right = mid;
            }
        }
        return left;

作者:liweiwei1419,感谢分享!!!
posted @ 2023-02-22 00:20  ku然  阅读(98)  评论(0编辑  收藏  举报