为什么二分法不建议使用 (right + left)除2?
- November 20, 2020
每天一个小知识,不定期更新
一、问题
事情是这样的,由于我最近在刷题,刷到这样一道题:
你是产品经理,目前正在带领一个团队开发新的产品。
不幸的是,你的产品的最新版本没有通过质量检测。
由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。
实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-bad-version
乍一看,题目是挺简单的。 直接采用二分法就可以立马解决。 于是我潇潇洒洒的写下
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1, right = n;
while(left < right){
int mid = (right + left) / 2;
if (isBadVersion(mid)){
right = mid;
}
else {
left = mid + 1;
}
}
return left;
}
}
也许,你看到我的代码,你会想没错啊!!!
自己也会这样写。 是的在我之前所有的二分法都是这样写的。
当我满怀信心的点了提交,然而
运行失败:
Time Limit Exceeded
测试用例:2126753390
1702766719
?????????Why
二、分析
须知:Integer
都有自己的表示范围,他有定义MAX_VALUE
和MIN_VALUE
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
,当我们的数超过他所规定的最值时,Integer将无法表示。
因此在我们上述算法中,
- left <=
MAX_VALUE
和 right <=MAX_VALUE
是肯定的 - 但是left+right <=
MAX_INT
我们无法确定,所以会造成栈溢出。
那么知道问题所在,我们怎么修改我们的代码呢?
我们可以使用
int mid = left + (right - left) /2;
来代替
int mid = (right + left) / 2;
他们最后的结果都是一致的,却能够有效的避免栈溢出问题。
三、结论
所以在我们使用二分法时,我们可以使用left + (right - left) /2
来代替(right + left) / 2
,来避免栈溢出。