二分查找算法

二分查找介绍

  • 二分的本质是找到区间的一个分界点,使得包含分界点的区间满足某个性质,不含分界点的区间不满足这个性质,二分查找是基于有序序列的查找算法。
  • 二分查找的高效之处在于,每一步都可以去除当前区间的一半元素,因此其时间复杂度时O(logn)。
  • 举个例子:图书馆自习的时候,一女生背着一堆书进阅览室,结果警报响了,大妈让女生看是哪本书把警报弄响了,女生把书倒出来,一本一本的测。大妈见状急了,把书分成两份,第一份过了一下,响了。又把这一份分成两份接着测,三回就找到了,大妈用鄙视的眼神看着女生,仿佛在说 O(N)和 O(logN)都分不清。
  • 二分思想:每次将范围缩小一半 , 确定范围内答案会被覆盖到 , 当长度为1时,就是答案,整数二分模板要保证有解,不同题目可能会无解

基本的二分查找应用

查找序列中是否存在某条件的元素
例如二分查找常用于解决最大值最小化 和 最小值最大化问题

二分查找解释

  • 首先,区间是有序的有单调性的,但二分本质不是单调性,有单调性一定可以二分,但可以二分不一定非要有单调性。
    若要查到的一个数比目标值大,则右半部分的值也比目标值大,那么右半部分就可以舍弃了。同样查找的值比较小,就可以舍弃左半边。
  • 二分查找要满足
    1.当前待查找序列,肯定包含目标元素 2.每次待查找序列的规模都会变小。
  • 图解(借鉴于acwing yxc)
    要找到一个性质可以将区间一分为二,一半满足一半不满足要求,则可以寻找绿色或者红色的边界。当在不同的边界,那么就产生不同的模板
    在这里插入图片描述

对于红色边界
是来寻找小于等于目标值的数
在这里插入图片描述
绿色边界
是来寻找大于等于目标值的数,因此左边界 l 要一直向右
这里为什么要mid = l + r +1 >> 1呢,相加的整数的除法是向下取整,当l = r - 1,若补不上1,向下取整 mid = l,会陷入死循环。
补上后mid会等于r,即[r,r]。
在这里插入图片描述

整数二分查找模板

二分查找模板有两个 , 一是尽量往左找,二十尽量往右找
整数的二分查找会更ex人 , 看起来很简单 ,但经常会出错,要么纠结在mid是否+1 , 要么纠结是否加等号。一个简便方法 , 这里坐标记left -> l,right ->r.
本质一点就是观察更新区间的时候,写的是l = mid还是r = mid,如果是第一种,那么就是区间向右缩小,便是求末位置,反之便是求初始位置。

//模板一
	while (l < r)
    {
        int mid = l + r >> 1;	//(l+r)/2
        if (check(mid))  r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
   模板二
   	while (l < r)
    {
        int mid = l + r + 1 >> 1;	//(l+r+1)/2
        if (check(mid))  l = mid;
        else r = mid - 1;
    }

简单总结下这两个模板
只要是往左找答案,就用第一个模板,mid不用加一,r=mid,l减一;
只要是往右找答案,就用第二个模板,mid要加一,l=mid,r要减一;

二分查找模板题

在这里插入图片描述
在这里插入图片描述
查找元素在序列中的始末位置,很典型的二分查找例题,查找初始位置,套用模板一,查找末位置,套用模板二。

# include <iostream>
using namespace std ; 
const int N = 1e5 + 10 ;
int a[N];
int main(){
	int n , m , k;
	cin >> n >>m; 
	for (int i = 0 ; i < n ; i ++)
		cin >> a[i];
	while ( m -- ){
		cin >> k ;
		int l = 0 , r = n - 1 ;  
		while ( l < r ){//先查找第一次出现的位置
			int mid = r + l >> 1;
			if (a[mid] >= k)  r = mid ;
			else l = mid + 1 ;
		}
		if (a[l] != k)	cout << "-1 -1" << endl;// 查找不到元素,直接结束查找末位置
		//当始位置能够被查到,继续进行查找末位置
		else {
			cout << l << " ";
			l = 0 , r = n - 1 ; // 查找大于等于目标值的数,mid要加以避免向下取整死循环
			while ( l < r ){
				int mid = r + l + 1 >> 1 ;
				if ( a[mid] <= k ) l = mid ;
				else r = mid - 1 ;
			}
			cout << l << endl ;
		}
	}
	return 0 ; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值