LeetCode_278第一个错误的版本

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_26458903/article/details/89674983

题目描述:

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。 

思路:

这道题主要考察的是二分查找,还有就是求平均值防溢出的点

  int min = 1;
        int max = n;
        if (isBadVersion(1)) return 1;

        while (min <= max) {
            //int mid = (min + max) / 2;    这样写会导致溢出
            int mid = min + (max - min) / 2;
            if (isBadVersion(mid)) {
                max = mid - 1;
            } else if (!isBadVersion(mid)) {
                min = mid + 1;
            }
        }
        return min;

移位操作和求平均值防溢出

求平均值的写法通常要写成这样,为了防止溢出:

int mid = min + (max - min) / 2;

我们来看一下移位操作:

>>> 表示无符号右移,>> 表示有符号右移,右移n位相当于除以2的n次幂。
两者的区别在于右移后,>>> 最高位补0,>> 正数最高位补0负数补1。
所以对于正数来讲,两者没有区别,但对于负数应该使用后者。

溢出的情况比较特殊,具体到求平均值的情况,两个int类型正整数相加,即使发生溢出也至多1位,就是说符号位会变成1但不会再产生进位了,因此并不会丢失精度。这个时候我们可以把它看做无符号整数,然后使用无符号右移1位,相当于除以2,高位补0不再溢出,所以结果还是对的。但如果我们使用有符号右移,它会被看成一个负数,最高位补1,导致结果错误。

测试:

        int maxValue1 = Integer.MAX_VALUE;
        int maxValue2 = Integer.MAX_VALUE - 2;
        System.out.println("maxValue1:" + maxValue1);
        System.out.println("maxValue2:" + maxValue2);

        System.out.println((maxValue1 + maxValue2) / 2);
        System.out.println((maxValue1 - maxValue2) / 2 + maxValue2);

        System.out.println((maxValue1 + maxValue2) >> 1);
        System.out.println((maxValue1 + maxValue2) >>> 1);

输出:

maxValue1:2147483647
maxValue2:2147483645
-2
2147483646
-2
2147483646

结果符合预期,所以取平均值时,使用第二和第四种方式可以避免溢出。

猜你喜欢

转载自blog.csdn.net/qq_26458903/article/details/89674983