你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
思路:一眼望过去,管它三七二十一,先来个暴破再说?
public int firstBadVersion(int n) {
for (int i = 1; i <= n; i++) {
if(isBadVersion(i)) {
return i;
}
}
return n;
}
不用想,肯定超时了。leetcode上的简单题,一般都不需要怎么想,主要是考察细心程度,情况有没有考虑全而已。接下来用递归实现二分的
public int firstBadVersion(int n) {
return firstBadVersion(1, n);
}
public int firstBadVersion(int lo, int hi) {
if (lo == hi) {
return lo;
}
int mid = (hi - lo) / 2 + lo; //这里是为了防止int溢出
if (isBadVersion(mid)) {
return firstBadVersion(lo, mid);
} else {
return firstBadVersion(mid + 1, hi);
}
}
其实很是需要细心一点的,两数相加要考虑溢出的情况,这里可以使用(hi - lo ) / 2 + lo计算平均值,而不是直接使用(hi + lo) / 2计算。
另外,在递归结束条件和递归传参方法上也可以进行小改动。(如果传的时mid - 1,那么循环结束的条件必须是lo > hi而不能包括等于的情况)
public int firstBadVersion(int n) {
return firstBadVersion(1, n);
}
public int firstBadVersion(int lo, int hi) {
if (lo > hi) { //递归结束条件
return lo;
}
int mid = (hi - lo) / 2 + lo;
if (isBadVersion(mid)) {
return firstBadVersion(lo, mid - 1); //使用上面的条件,这里可以缩小范围
} else {
return firstBadVersion(mid + 1, hi);
}
}
根据递归也可以很快的写出迭代的实现方法
public int firstBadVersion(int n) {
int lo = 1, hi = n, mid ;
while (lo < hi) {
mid = (hi - lo) / 2 + lo;
if (isBadVersion(mid))
hi = mid;
else
lo = mid + 1;
}
return lo;
}
总结一下,这道题值得注意的点在于边界值的处理还有两数相加溢出的情况。