(三十)二分查找(Binary Search)

一、前言

大多数人在小时候应该都玩过数字扫雷。比如在1-100中任意选择一个数字作为雷,比如82,第一个人从50开始查找,裁判会说“数字小了”,第二个人就得说一个50-100的数字,比如75,裁判宣布“太小了”。第三个人从75-100选择一个数,90,“太大了”,第四个人在75-90之中选择一个数字88,“太大了”。第五个人75-88选择80,“太小了”,直到第X人猜测的数字等于那个数字,裁判就宣布“第X个人踩雷了”。这就是一个简单的二分查找。

二、正文

(一)什么是二分查找?

二分查找(Binary Search),又叫折半查找,顾名思义就是每一次折掉一半进行查找

线性查找(Linear Search),这种查找在以前我们经常用

二分查找只能在有顺序、可随机访问的容器中使用,set容器和map容器就被淘汰了。虽然功能受限,但时间复杂度低,只有 O ( l o g 2 n ) O(log_2n) O(log2n)

线性查找适用于所有容器,但时间复杂度高 O ( n ) O(n) O(n)

(二)二分查找如何查找?

获得查找数组 A A A,数组项数 n n n,查找内容 x x x A A A数组从第一项开始存数,且保证数组已经从小到大排完了序)

  1. 首先定义两个变量 l e f t = 1 , r i g h t = n left=1, right=n left=1,right=n
  2. l e f t ≤ r i g h t left ≤ right leftright的情况下执行下面代码:
  3. 获得中间位置 m i d = ( l e f t + r i g h t ) ÷ 2 mid=(left+right) \div 2 mid=(left+right)÷2
  4. 如果中心位置的值 A m i d > x A_{mid} > x Amid>x,说明太大了,就得减小: l e f t = m i d − 1 left = mid-1 left=mid1
  5. 如果中心位置的值 A m i d < x A_{mid} < x Amid<x,说明太小了,就得增大: r i g h t = m i d + 1 right = mid+1 right=mid+1
  6. 如果中心位置的值 A m i d = x A_{mid} = x Amid=x,说明查找到了,就输出: m i d mid mid,并结束程序
  7. 重复3-6步骤,直到退出循环或找到数字
  8. 如果退出了while循环,说明找不到 x x x(结束程序除外)

代码是这样的

#include <iostream>
using namespace std; 
int main() {
	int n; 
	cin >> n; 
	int a[n+1]; 
	for(int i=1; i<=n; i++)
		cin >> a[i]; 
	int l=1, r=n, x; 
	cin >> x; 
	while(l<=r) {
		int m = (l+r) >> 1; 
		//在C++中一个数右移n位 = 那个数除以2的n次方
		//同样,一个数左移n位 = 那个数乘2的n次方
		if(a[m] > x) //太大了
			r = m - 1; //缩小
		if(a[m] < x) //太小了
			l = m + 1; //增大
		if(a[m] == x) { //猜对了
			cout << m; 
			return 0; 
		}
	}
	cout << -1; 
	return 0; 
}

(三)时间复杂度

O ( l o g 2 n ) O(log_2n) O(log2n)
为什么呢?要知道 l o g 2 n log_2n log2n 2 n 2^n 2n两个有着互逆的关系

n n n 2 n 2^n 2n
0 0 0 1 1 1
1 1 1 2 2 2
2 2 2 4 4 4
3 3 3 8 8 8
4 4 4 16 16 16
5 5 5 32 32 32

你会发现 2 n = 2 n − 1 × 2 2^n = 2^{n-1} \times 2 2n=2n1×2,刚好是折半的关系,而 l o g 2 log_2 log2又互逆,所以时间复杂度就是 O ( l o g 2 n ) O(log_2n) O(log2n)

(四)相关函数

位于#include <algorithm>库中

  1. binary_search(begit, endit, num):第一项和第二项填起始和结尾的指针,第三项填要查找的数,返回类型bool,如果数组中确实存在数字num,那么返回true,否则返回false
  2. lower_bound(begit, endit, num):一样的参数,返回类型指针,返回第一个大于等于num的值的指针,减去数组起始指针后等于所在项数
  3. upper_bound(begit, endit, num):一样的参数,返回类型指针,返回第一个大于num的值的指针,减去数组起始指针后等于所在项数

因此使用函数可以这么写

#include <iostream>
#include <algorithm>
using namespace std; 
int main() {
	int n; 
	cin >> n; 
	int a[n+1]; 
	for(int i=1; i<=n; i++)
		cin >> a[i]; 
	int x; 
	cin >> x; 
	if(binary_search(a+1, a+n+1, x)==0) 
		cout << -1; 
	else 
		cout << lower_bound(a+1, a+n+1, x) - a; 
	return 0; 
}

三、结语

预览

  • 二十二:类(class)
  • 二十三:高精度运算
  • 二十四:算法进阶
  • 二十五:递归
  • 二十六:vector容器
  • 二十七:递推
  • 二十八:set容器
  • 二十九:map容器
  • 三十:二分查找(Binary Search, BS
  • 三十一:前缀和与差分
  • 三十二:栈(stack
  • 三十三:队列(queue)和双向队列(deque
  • 三十四:电脑基础知识
  • 三十五:链表
  • 三十六:树
  • 三十七:图
  • 三十八:预处理命令
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值