二分查找算法用于在一个有序的列表中查找某个值的位置,列表一般是升序排列的。循环比较中间值A[mid]和需要查找的值x,如果x小于等于中间值,那么在左边找;如果x大于中间值,那么去右边找。
二分查找的思想很简单,但是有很多种写法,我自己用的模板是仿照STL的二分函数,即有两个版本,一个是查找第一个大于等于x的值的下标,若找不到则返回尾后指针;另一个版本是查找第一个大于x的值的下标,找不到返回尾后指针。
我的自用二分模板
#include <bits/stdc++.h>
using namespace std;
// 找出第一个大于等于x的元素的下标
int search_lower_bound(int *A, int left, int len, int x) {
int mid, right = left + len;
while (left < right) {
mid = (left + right) / 2;
if (x <= A[mid])
right = mid;
else
left = mid + 1;
}
return left;
}
// 找出第一个大于x的元素的下标
int search_upper_bound(int *A, int left, int len, int x) {
int mid, right = left + len;
while (left < right) {
mid = (left + right) / 2;
if (x < A[mid])
right = mid;
else
left = mid + 1;
}
return left;
}
const int LEN = 8;
int A[LEN] = { 1, 1, 4, 5, 14, 19, 19, 810 }; //升序数组
int main() {
for (int i = 0; i < LEN; i++) cout << A[i] << " "; cout << endl;
int x;
while (cin >> x) {
int lo = search_lower_bound(A, 0, LEN, x);
int up = search_upper_bound(A, 0, LEN, x);
printf("A[%d] >= %d\n", lo, x);
printf("A[%d] > %d\n", up, x);
}
return 0;
}
两个函数的唯一区别就是 if (x < A[mid])
,如果带等于号,则返回第一个大于等于x的值的下标;若不带等于号,则返回第一个大于x的值的下标。
STL 二分函数
STL 二分查找函数更加灵活,它是模板函数,支持多种类型,包括自定义类型(需重载小于运算符),可以传lambda表达式自定义比较规则。
下面附上两个demo演示 STL 二分函数如何应用在自定义类型数组以及降序的整型数组。
降序数组demo
#include <bits/stdc++.h>
using namespace std;
int main() {
int A[] = {10,8,6,4,2}; // 降序数组
size_t x1 = lower_bound(A, A+5, 3, [](int a, int b) { return a > b; }) - A;
size_t x2 = upper_bound(A, A+5, 3, [](int a, int b) { return a > b; }) - A;
size_t x3 = lower_bound(A, A+5, 4, [](int a, int b) { return a > b; }) - A;
size_t x4 = upper_bound(A, A+5, 4, [](int a, int b) { return a > b; }) - A;
cout << x1 << endl; // 第一个小于等于3的元素下标
cout << x2 << endl; // 第一个小于3的元素下标
cout << x3 << endl; // 第一个小于等于4的元素下标
cout << x4 << endl; // 第一个小于4的元素下标
return 0;
}
自定义类型demo
#include <bits/stdc++.h>
using namespace std;
struct MyPair {
int a,b;
MyPair(int _a, int _b) : a(_a), b(_b) {}
bool operator<(const MyPair &other) {
if (a != other.a) return a < other.a;
return b < other.b;
}
};
int main() {
vector<MyPair> v{{1,2}, {3,4}, {5,6}};
int x = lower_bound(v.begin(), v.end(), MyPair(3,4)) - v.begin();
cout << x << endl;
return 0;
}