字节笔试题 leetcode 69. x 的平方根

题目如下图示:

                                                            更多精彩文章请关注微信公众号:TanLiuYi00

题目要求非负整数 x 的平方根,相当于求函数 y = √x 中 y 的值。

函数 y = √x  图像如下:

从上图中,可以看出函数是单调递增的,满足二分查找的条件(区间是有序的),所以可以考虑用二分查找去做。

解题细节

1、由于当 x 为非负整数时,其平分根一定在区间 [1, x/2 + 1] 内,所以为了缩小查找的范围,二分的左右边界分别取 left = 1 和 right = x / 2 + 1,而不取 0 和 x;

2、为了防止在查找过程中比较 mid * mid 和 x 中,前者值太大,超出整型范围而溢出,取 mid 与 x / mid 进行比较(mid 为 1 中 的 left 和 right 的和的平均值,故不会等于 0)。

Show me the Code

// C++ 版本
class Solution {
public:
    int mySqrt(int x) {
        int left = 1, right = (x >> 1) + 1;
        while (left <= right) {
            /* 防止溢出 */
            int mid = left + ((right - left) >> 1);
            /* mid 大于 √x ,在 mid 前半区间查找 */
            if (mid > x / mid) {
                right = mid - 1;
            /* mid 小于 √x ,在 mid 后半区间查找 */
            } else if (mid < x / mid) {
                left = mid + 1;
            /* mid 等于 √x ,查找到直接返回 */
            } else {
                return mid;
            }
        }
        return right;       
    }
};
# golang 版本
func mySqrt(x int) int {
    l, h:= 1, x/2 + 1
    for l <= h {
        mid := l + (h - l) / 2
        if mid == x / mid {
            return mid
        } else if mid > x / mid {
            h = mid - 1
        } else {
            l = mid + 1
        }
    }

    return h
}

说明

  1. right = x / 2 + 1,而不是 right = x / 2,因为当 x = 1 时,如果 right 取 x / 2 的话,由于 x/2 = 0,此时 left = 1 大于 right,直接循环跳出,导致 x 的平方根为 0 而不是 1 出错

  2. 最后是 return right 还是 return left,可以通过调试得出,循环退出的条件是 left = right + 1,当 x = 8 的时候,题目要求返回的是 2 ,如果最后是 return left 的话,返回的结果是 3 ,所以返回的是 right 。

进一步补充
可能有些童鞋会问到,你上面写的循环的条件为何是 left <= right 而不是 left < right,其实这两个条件都可以,主要区别在于:

循环结束的条件不一样,前者是 left = right + 1 后者是 left = right。

定义的查找区间的右边界取值不一样,前者右边界取值为 x / 2 + 1,后者为 x / 2 + 2。

这里会提到一个循环不变量的概念:

循环不变量
1. 初始化:它在循环的第一轮迭代开始之前,应该是正确的。

2. 如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。

3. 当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

所以本题循环的条件可以是 left <= right 也可以是 left < right,关键是需要一直维护对应的区间 [left, right] 或 [left, right)。下面补充一个循环条件为 left < right 的 C 语言代码,最后返回的是 right - 1,是因为:

1. 定义的查找区间是左闭右开的,即 [left, right),取不到右边界 right;当 left == right 时,循环退出,但是 right 取不到,所以取 right - 1;

2. 通过调试也能得出。

Show me the Code

int mySqrt(int x){
    int left = 1, right = x / 2 + 2;
    //  循环不变量   始终维持在区间 [left, right) 中查找,当 left = right 时,区间为空,查找结束
    while (left < right) { 
        //  防止溢出                     
        int mid = left + ((right - left) >> 1);
        //  mid 大于 √x ,在 mid 前半区间 [left, mid) 中查找
        if (mid > x / mid) {                     
            right = mid;
        //  mid 小于 √x ,在 mid 后半区间 [mid + 1, right) 中查找
        } else if (mid < x / mid) {              
            left = mid + 1;
        //  mid 等于 √x ,代表查找到 target,则直接返回
        } else {                                 
            return mid;
        }
    }

    return right - 1;
}

更多精彩内容,请关注  微信公众号: TanLiuYi00  

欢迎大家提出意见或建议,谢谢!

猜你喜欢

转载自blog.csdn.net/Tanyongyin/article/details/112093970