前言
某种算法的实际应用,核心就是要找到当前算法的使用前提,基于其算法核心,对数据进行重组、改造,最终输出;
二分法
二分法顾名思义,通过每次取中间数进行对比,从而得到想要的数据,这就不必赘述了吧!
下面来讲一下二分法的算法核心:
- 确定为有序数组,
- 利用三个数进行查找,所以先确定三个值,开始值、末尾值、中间值;中间值需要进行计算得来;
- 将中间值与目标值进行比较;
- 中间值 < 末尾值/目标值,以中间值的下一位作为末尾值,开始值不变;
- 中间值 > 末尾值/目标值,以开始值的前一位作为开始值,末尾值不变;
- 目标值 = 中间值,则为想要的结果;
注意⚠️
- 数组必须为有序数组;
- 确定数组中是否有相同元素,若有,则需要自己处理掉,即无重复元素;
🐲第一个错误的版本
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4
输出:4解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
分析:
由于此数组本身就是数字,且自增,所以,可以设置开始值为left = 1,末尾值right=n,中间值mid为向下取整的中间数;然后,就利用中间值,判断是否为正确版本,是正确版本,则正确区间为[mid+1, right],不是正确版本,则
正确区间为 [left, mid];当left=right,则输出正确值;
var solution = function(isBadVersion) {
return function(n) {
let left = 1, right = n;
while (left < right) {
// 循环直至区间左右端点相同
const mid = Math.floor(left + (right - left) / 2); // 防止计算时溢出
if (isBadVersion(mid)) {
right = mid; // 错误版本,答案在区间 [left, mid] 中
} else {
left = mid + 1; //正确版本 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即为答案
return left;
};
};
🐲寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。题目来源:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array
分析:
由于目标值可能大于最大的数组元素,所以right值为数组长度,而不是数组的长度-1;
var searchInsert = function(nums, target) {
let left = 0;right = nums.length;
while(left < right){
let mid = Math.floor(left+ (right-left)/2);
if(nums[mid] < target){
left = mid+ 1;
}else{
right = mid;
}
}
return left;
};
结语
二分法,主要逻辑就是通过中间值和目标值的比较,然后缩小数组范围,最终得到答案,从问题中提炼出这个含义,就可以进行二分法的使用了,当然,在某些情况下,二分法也并不算得上最优解,按需取用即可!