为什么有一些二分查找取 mid 的时候要加 1

Posted liweiwei1419

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么有一些二分查找取 mid 的时候要加 1相关的知识,希望对你有一定的参考价值。

为什么有一些二分查找取 mid 的时候要加 1

以「力扣」第 69 题:x 的平方根为例。

题目描述:实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1

输入: 4
输出: 2

示例 2

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

数据范围

  • 0 <= x <= 2^31 - 1

这道问题的代码:

public class Solution {

    public int mySqrt(int x) {
        // 特殊值判断
        if (x == 0) {
            return 0;
        }
        if (x == 1) {
            return 1;
        }

        int left = 1;
        int right = x / 2;
        // 在区间 [left..right] 查找目标元素
        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            // 注意:这里为了避免乘法溢出,改用除法
            if (mid > x / mid) {
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else {
                // 下一轮搜索区间是 [mid..right]
                left = mid;
            }
        }
        return left;
    }
}

由于我们只是讲解 mid 为什么要 加 1 1 1,如何分析和解决这道问题,可以查看 题解

对着错误的测试用例打印出变量 leftrightmid 的值看一下就很清楚了。

public class Solution {

    public int mySqrt(int x) {
        // 特殊值判断
        if (x == 0) {
            return 0;
        }
        if (x == 1) {
            return 1;
        }

        int left = 1;
        int right = x / 2;
        // 在区间 [left..right] 查找目标元素
        while (left < right) {
            // 取中间数 mid 下取整时
            int mid = left + (right - left ) / 2;

            // 调试语句开始
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("left = " + left + ", right = " + right + ", mid = " + mid);
            // 调试语句结束

            // 注意:这里为了避免乘法溢出,改用除法
            if (mid > x / mid) {
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else {
                // 下一轮搜索区间是 [mid..right]
                left = mid;
            }
        }
        return left;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int x = 9;
        int res = solution.mySqrt(x);
        System.out.println(res);
    }
}

控制台输出:

left = 1, right = 4, mid = 2
left = 2, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3

在区间只有 2 2 2​ 个数的时候,本题 ifelse 的逻辑区间的划分方式是:[left..mid - 1][mid..right]。如果 mid 下取整,在区间只有 2 2 2​ 个数的时候有 mid 的值等于 left,一旦进入分支 [mid..right] 区间不会再缩小,出现死循环。

解决办法:把取中间数的方式改成上取整。

最近正在 B 站讲解《算法不好玩》系列教程,地址,以新手视角讲解算法与数据结构,通俗易懂且不失严谨。公众号:算法不好玩。感谢大家支持!

以上是关于为什么有一些二分查找取 mid 的时候要加 1的主要内容,如果未能解决你的问题,请参考以下文章

为什么有一些二分查找取 mid 的时候要加 1

二分法查找数据

递归1--二分查找

算法题16 二分查找及相关题目

「算法笔记」一文摸秃二分查找

二分法查找