👉目录
一、 题目连接 704. 二分查找 - 力扣(LeetCode)
四、lower_bound 、 upper_bound、binary_search (推荐)
经过两个月(甚至我觉得更久)的挣扎,终于对基本的算法有了一个稍为全面的理解,理论知识+c++语法的不断练习+刷题背诵模版,但是学到最后对最初的简单算法反而有点淡忘了·····自己是真没学算法的天赋,只能不断一遍遍重复努力了·····
我的问题(不知道大家会不会有):①接触到新算法后对旧的算法就会忘【可能一开始学的时候就懵懵懂懂的···】②做过的题还是不会做【emm所以我才以博客的形式记录,方便回顾,就像高中数学一样,大量无厘头的刷题不如有一个属于自己的错题本!】③看到新题有思路也能写代码但结果不是报错就是写一半写不下去了【自己静不下心emm再加上语法不熟练 知识点不熟练】
所以我打算从头开始温习一遍(距离保研机试还有差不多5-6月的时间)加油吧!!😃👍
一、 题目连接 704. 二分查找 - 力扣(LeetCode)
二、理论学习-基础二分查找
写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
下面我用这两种区间的定义分别讲解两种不同的二分写法。
(1)[left, right]
因为定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;//[]
while(left<=right)
{
int middle=(left+right)/2;
if(nums[middle]<target) left=middle+1;
else if(nums[middle]>target) right=middle-1;
else return middle;
}
return -1;
}
};
(2)[left, right)
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size();//[ )
while(left<right)
{
int middle=(left+right)/2;
if(nums[middle]<target) left=middle+1;
else if(nums[middle]>target) right=middle;
else return middle;
}
return -1;
}
};
三、基础算法总结
①先定义left right 【区间的定义】
②进入循环 【注意循环条件】
③将target 与 nums[middle] 进行比对,结合①对区间的定义 更新左右区间 【主体部分】
四、lower_bound
、 upper_bound、
binary_search (推荐)
C++ STL 提供了 lower_bound
和 upper_bound
进行二分查找,它们位于 <algorithm>
头文件中
binary_search()
只能用于已排序的容器
如何转换为索引?
-
left - nums.begin()
计算 第一个target
出现的索引。 -
right - nums.begin() - 1
计算 最后一个target
出现的索引
五、题目练习
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int res = lower_bound(nums.begin(),nums.end(),target)-nums.begin();
return res;
}
};
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res = {-1, -1}; // 默认返回 [-1, -1]
auto left = lower_bound(nums.begin(), nums.end(), target);
if (left == nums.end()||*left!=target) {
//*left 是该迭代器指向的值可能返回一个大于 target 的元素,而不一定是 target
return res; // target 不存在,直接返回
}
auto right = upper_bound(nums.begin(), nums.end(), target);
res[0] = left - nums.begin();
res[1] = right - nums.begin() - 1;
return res;
}
};
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,q;
cin>>n>>q;
vector<int> nums(n);
for(int i=0;i<n;i++) cin>>nums[i];
while(q--)
{
int k;
cin>>k;
bool have=binary_search(nums.begin(),nums.end(),k);
if(!have) cout<<"-1 -1"<<endl;
else//有钙该元素
{
int res1=lower_bound(nums.begin(),nums.end(),k)-nums.begin();
int res2=upper_bound(nums.begin(),nums.end(),k)-nums.begin()-1;
cout<<res1<<" "<<res2<<endl;
}
}
return 0;
}