持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情
题目
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
解题思路
关键字:第一个错误版本;方法判断;减少调用次数;错误版本在n个版本中
关键信息:二分法;有序遍历
题目比较重要的一点是错误版本是在n当中的。
递归解法
递归解法必然是简洁代码量最少的情况。但由于调用次数没有优化导致运行时间超时。但递归思想在算法解题中尤为重要,练习使用递归还是有好处的。
- 递归终结条件:当版本判断上不是错误版本时下一个版本就是错误版本。
- 循环段落:执行上一个版本。
- 返回结果是版本号。
public int firstBadVersion(int n) {
if(!isBadVersion(n)) return n + 1;
int version = firstBadVersion(n - 1);
return version;
}
递归在有序遍历中并不太适用,主要是在执行效率上并不高效。因此递归算法不作为最优解但是可以参考的算法思路。
二分查找解法
题目要求尽量减少接口调用;因此并不会对整个版本号都去做遍历请求。所以有序遍历一般会用到二分法来处理实现(双指针算法,首尾指针不断分割收敛遍历范围)。
- 创建左右指针分别指向首尾版本号。
- 循环获取中间版本号通过isBadVersion方法判断是否是错误版本
- 如果是错误版本则右边指针收敛到错误版本;若不是错误版本则左边指针收敛到中间版本+1
- 循环终止为左右指针重合。
public int firstBadVersion(int n) {
int left = 1;
int right = n;
int res = n;
while (left <= right) {
int mid = left + ((right - left) / 2);
if (isBadVersion(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
二分法有效减少遍历次数通过不断收敛只需要做O(logn) 次即可求解。
二分查找思路
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
普通遍历
普通遍历的方法其实就很简单,遍历整个数组找到希望的值。时间长度和数组长度有关。
public int search(int[] nums, int target) {
int index = 0;
for(int num : nums){
if(num == target){
return index;
}
index ++;
}
return -1;
}
二分查找
上题错误版本就是采用二分查找算法思路实现,全局遍历效率肯定不高但利用二分法就能实现高效查询。基本的二分查找模版代码如下所示:
public int search(int[] nums, int target) {
int leftIndex = 0,rightIndex = nums.length - 1;
while(leftIndex <= rightIndex){
int mid = ( rightIndex - leftIndex ) / 2 + leftIndex;
int midValue = nums[mid];
if(midValue == target){
return mid;
}else if(target > midValue){
leftIndex = mid + 1;
}else {
rightIndex = mid - 1;
}
}
return -1;
}
参考
- 来源:力扣(LeetCode)