前言
二分查找是效率较高的查找方法,可以适用于任何类型的查找。效率为O(logn)当n非常大的时候,效率是比O(n)高很多的。
在二分查找整数的时候,一般左边=右边的时候就是找到了。如果左边>右边,那么就是找不到,一般是返回-1。
二分查找的难点在于,左右边界的取值以及判断条件的写法。
一个例题
AC代码
#include <bits/stdc++>
using namespace std;
int arr[1000000];
int main() {
int len, x;
cin >> len;
for (int i = 0; i < len; i++) {
int a;
cin >> a;
arr[i] = a;
}
int l = 0, r = len - 1;
cin >> x;
while (l <= r) {//???
int mid = (l + r) / 2;
if (arr[mid] > x) {
r = mid - 1;
} else if (arr[mid] < x) {
l = mid + 1;
} else {
cout << mid + 1;
return 0;//立即停止
}
}
cout << -1;
}
这个程序如果判断条件是l<r就只能过一半。
关于mid的写法
有三种写法
- mid=(l+r)/2 这是最常规的写法
- mid=(l+r)>>1 位运算相当于除以2
- mid=l+(r-l)/2 可以防止l+r溢出(越界)的情况 因为r与l可能在int的边界 可能会超过int
比较指标
一般是用已经排好序的数组作为比较指标的,有时候也可以用函数作为比较指标,找边界值。
此题就采用了f(n)=n(n+1)/2的函数。
while是小于等于还是小于
这与区间是闭区间还是左闭右开的区间有关。如果带上了等号,那么每次查找的都是一个闭区间,显然我们查找的时候必然带上了最右边的数,所以要加上等号。
但我们也可以采用小于号,即左闭右开的写法。那么此时需要把初始化的r向右移一位。在判断的时候就不需要把r向mid的左边移一位了,因为此时并不包括r。
所以小于等于或者小于都可以的,但是一般用小于等于比较多。
例题 查找左边界
大致思路如下 第一次画 蛮丑的说实话
#include <iostream>
using namespace std;
int a[100000];
int binary_search(int x, int n) {
//不用传入数组 因为数组是地址 两个不相干函数之间都看得到
int l = 0, r = n - 1, mid;
while (l <= r) {//闭区间查找法 比较指标为数组a
mid = (l + r) / 2;
if (a[mid] > x)
r = mid - 1;
else if (a[mid] < x)
l = mid + 1;
//若找到了 我们必须还继续向左找才可以
else
r = mid - 1;
}
//跳出来后 如果找到了l对应的数一定是x 如果不是 则未找到
if (a[l] == x)
return l + 1;
else
return -1;
}
int main() {
int n;//数组的长度
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int q;//次数
cin >> q;
int x;//每次要查找的数
for (int i = 0; i < q; i++) {
cin >> x;
cout << binary_search(x, n) << " ";
}
}