二分查找详解
- 2021 年 12 月 16 日
- 筆記
将二分查找问题分为两种情况讨论,一种是有序序列中不存在重复元组,另一种是有序序列中存在重复元素
一、不存在重复元素
分为三种情况讨论,>=(大于或者等于)、>(大于)和<=(小于或者等于)
有序序列为{1, 3, 4, 5, 7, 8, 9, 10, 12}
大于或者等于target
可以用lower_bound函数代替
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] == target){
return mid;
}else if(a[mid] < target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
注意以下几点:
- 当a[mid]等于target时,直接返回mid;
- 当a[mid]小于target时,left = mid + 1;
- 当a[mid]大于target时,right = mid;
- 循环条件是left < right,否则会出现死循环的情况。
大于target
可以用upper_bound函数代替
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] <= target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
注意以下几点:
- 当a[mid]小于或者等于target时,left = mid + 1;
- 当a[mid]大于target时,right = mid;
- 循环条件是left < right,否则会出现死循环的情况。
小于或者等于target
相比于大于target的情况,只需要将返回值改为left – 1即可
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 4, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] <= target){
left = mid + 1;
}else{
right = mid;
}
}
return left - 1;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
二、存在重复元素
分为两种情况讨论,寻找第一个小于或者等于target的位置和第一个大于target的位置
有序序列为{1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12}
第一个大于target的位置
这种情况跟不存在重复元素下对应的情况一致,直接套用上面的代码即可
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] <= target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
第一个大于或者等于target的位置
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> a = {1, 3, 3, 4, 5, 5, 7, 8, 9, 10, 12};
int n = (int)a.size();
int target;
function<int(int)> found = [&](int target){
int left = 0, right = n - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(a[mid] == target){
if(mid == left || a[mid - 1] < target){
return mid;
}else{
right = mid;
}
}else if(a[mid] < target){
left = mid + 1;
}else{
right = mid;
}
}
return left;
};
while(cin >> target){
cout << found(target) << endl;
}
return 0;
}
相比于不存在重复元素时对应的情况,需要对a[mid] == target的情况展开讨论,讨论情况如下:
- 如果mid恰好是最左边left位置的元素,那么直接返回mid即可
- 如果mid不是最左边left位置的元素,但是mid左边的第一个元素小于target,即a[mid – 1] < target,那么也是直接返回mid即可
- 其余情况下,right = mid。