1.定义
二分查找算法是一种效率较高的查找算法,每次查找能把查找范围缩小一半,故也称为折半查找(查找前提:所查区间有序)。
2.举个栗子
现在小明在玩一个猜数游戏,游戏会在(0 ,100]内给定一个数,现在要小明猜这个数,如果小明猜对了,游戏会提示猜对了,游戏结束,如果小明猜错了,它会提示小明的猜的数比答案小了或者大了,要怎么猜才能尽快猜出答案。
现假定答案是69, 我们要在0 - 100 内去猜。
一.如果不看提示
- 1.最好的情况,小明是欧皇, 第一次就猜69(一发入魂),猜的次数为1。
- 2.最坏的情况,小明炒鸡倒霉, 前99次都没猜中, 最后一次中了,猜的次数为100。
二.看提示的话
1.小明猜的是3, 假定答案是2的话,他就可以根据提示在(0,3)中猜(极好的情况),但答案是99的话,他就要在(0, 100】中猜了,这样一来,能不能尽快猜中就得看运气了, 在从数学的角度上看,想要尽快猜中靠的不是运运气,而是策略,因此二分查找来啦。二分查找每次查找的都是中间值,这样一来就能把所查找区间减小一半。
例,还是查69, 第一次二分的查找找中间值50, 比69小了,第二次就从【50, 100】内查找,第二次找中间值75,查找范围编成了(50, 75)范围就又小了一半。 如此反复。 时间复杂度是log100, 大概10 次左右。 而随机查找复杂度充满不确定性(看是欧皇还是非酋),反正比二分大。
1. 普通二分查找
定义
顾名思义,随机查找虽然能查找但所找的元素元素位置随机。
例子 在 a[5] = {0,1 2 2 2 5 }中找x (x =2), 随机二分查找会找到它第一次找到该元素的位置并返回
查找过程
l = 1, r = 5, mid; // l 是第一个元素的下标,r是最后一个元素的下标, mid 是中间元素的下标
mid =(1 + 5 )/ 2 = 3
a[mid] = 2
a[mid] == x
返回 3
虽然随机二分查找返回了该元素出现的位置,但该元素不能确定是在序列中第一次出现或最后一次, 即出现次数不定。
随机二分查找函数模板
#include<iostream>
#include<algorithm>
using namespace std;
int a[100], n;// a[]为查找的区间, n为区间元素个数
int find(int x) {//随机查找x
int l = 1, r = n, mid;
while (l <= r) {
mid = (l + r)>>1;
if (a[mid] == x) return mid;
a[mid] > x ? r = mid - 1 : l = mid + 1;
}
return -1;
}
ps:偶的二分查找的下标是从1开始的,实际使用要看数组的定义情况来确定二分边界。
2.二分查找最左(第一次出现)
即查找序列中该元素第一次出现的位置 原理
在随机查找的基础上做一些修改,在找到它第一次找到该元素的位置时,记录该位置,然后再从该位置之前在进行查找 即在if (a[mid] == x) return mid;
中加一句r = mid - 1;
int findzuo(int x) {
int l = 1, r = n, mid, ans = -1;//ans记录出现位置
while (l <= r) {
mid = (l + r) / 2;
if (a[mid] == x) {//找到了
ans = mid;//记录下出现的位置
r = mid - 1;//在从【l, mid- 1]中找是否还会出现x
}
else if (a[mid] > x) {
r = mid - 1;
}
else l = mid + 1;
}
return ans;
}
二分查找最左模板题
3.二分查找最右(最后一次出现)
即查找序列中该元素第一次出现的位置 原理
在随机查找的基础上做一些修改,在找到它第一次找到该元素的位置时,记录该位置,然后再从该位置之后在进行查找
if (a[mid] == x) return mid;
中加一句 l = mid +1;
int findyou(int x) {
int l = 1, r = n, mid, ans = -1;//ans记录出现位置
while (l <= r) {
mid = (l + r) / 2;
if (a[mid] == x) {//找到了
ans = mid;//记录下出现的位置
l = mid + 1;//在从【mid-+1, l]中找是否还会出现x
}
else if (a[mid] > x) {
r = mid - 1;
}
else l = mid + 1;
}
return ans;
ps:二分查找是在有序序列的基础上进行的查找,序列必须有序。
4.二分查找练习题
待补