二分法第一种写法
第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] (这个很重要非常重要)。
区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:
-
while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
-
if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
#include<iostream>
#include<vector>
using namespace::std;
int solution(vector<int>& v, int target)
{
//用双闭口二分法
int left = 0;
int right = v.size() - 1;
while (left <= right)
{
int middle = left + ((right - left) / 2);
if (v[middle] > target)
{
right = middle - 1;
}
else if (v[middle] < target)
{
left = middle + 1;
}
else
return middle;
}
return -1;
}
int main()
{
vector<int> v;
int num;
v.push_back(-1);
v.push_back(0);
v.push_back(3);
v.push_back(5);
v.push_back(9);
v.push_back(12);
cin >> num;
cout<<solution(v, num);
return 0;
}
二分法第二种写法
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
有如下两点:
-
while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
-
if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
#include<iostream>
#include<vector>
using namespace::std;
int solution(vector<int>& v, int target)
{
//用双闭口二分法
int left = 0;
int right = v.size() - 1;
while (left <= right)
{
int middle = left + ((right - left) / 2);
if (v[middle] > target)
{
right = middle;
}
else if (v[middle] < target)
{
left = middle + 1;
}
else
return middle;
}
return -1;
}
int main()
{
vector<int> v;
int num;
v.push_back(-1);
v.push_back(0);
v.push_back(3);
v.push_back(5);
v.push_back(9);
v.push_back(12);
cin >> num;
cout<<solution(v, num);
return 0;
}
二分法是非常重要的基础算法, 其实主要就是对区间的定义没有理解清楚,在循环中没有始终坚持根据查找区间的定义来做边界处理。区间的定义就是不变量,那么在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则。