二分法入门(c++)
什么是二分法
二分法,在计算机中也称之为二分查找法,用于查找有序数列中的数值,核心原理就是利用数组已经有序的特点,取数组中间值与目标值作比较,如果目标值大,数组左边界望右移,反之右边界往左移。举例说明,在数组1,5,6,8,9中;如果想要查找8所在的位置,我们怎么做?当然我们可以用一个循环逐一比较得出结论,但是,如果样本数据很大(几百万),而目标数值恰好在后面,一个个去查找时间复杂度就比较高,那么此时就要寻求更省时的办法,即今天介绍的二分法,它能实现降维打击,将时间复杂度变为对数级。还是以前面的例子说明,我们设数组名为a,如果第一次取中间那个数,a[2]=6,一下子就确定目标值在6的右边,再按第一次进行操作,取a[3]与a[4]中间值,那么可以找到目标值的位置。
在🌳面前,二分法的可以虐杀循环。
leetcode例题实现
1. 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
分析:给定数组查找对应值的下标,无对应数返回-1.
int search(vector<int>& nums, int target) {//vecter是c++中的标准库模板,在这里用vector可以读入一系列连续的数,就相当于读入一段数组,下标从0开始,数值大小可以用nums.size()测出
int left=0,right=nums.size()-1;//确定左右边界
while(left<=right){
int mid=(left+right)/2;//每次找出中间下标
if(nums[mid]==target)
return mid;
else if(nums[mid]<target)
left=mid+1;//目标值偏大,左边界右移
else right=mid-1;//目标值偏小,右边界左移
}
return -1; //不满足返回-1
}
};
**2**
```你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 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 是第一个错误的版本。
示例 2:
输入:n = 1, bad = 1
输出:1
分析:返回值正确的是问题产品,用二分法确定第一个问题产品。
在这// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int left=1,right=n;
while(left<right){
int mid=left+(right-left)/2;//特别关注,这种写法和(l+r)/2的区别是,后者在数据过大时会溢出,计算机会报错。
if(isBadVersion(mid))
right=mid;//不是所有情况右边界都要在中值的基础上减一,本题中,如果第一次中间恰好为问题产品,如果还是按照案例一,就会出现问题。
else left=mid+1;
}
return left;
}
};
反思总结
二分法相对来说是一种简单的可复制性强的算法,在写代码的过程中,只需要把握几个关键的量,左边界,右边界,中间值和循环条件,再结合题目具体情境改变一些量即可。
2022年2月23号闲来无事总结一下最近学习的知识。