二分查找 的 各种模板

二分查找 的各种模板

算法简介

​ 二分查找是二分法的一种应用,用来解决有序区间的元素查找问题。

​ 每次判断区间中点,根据判断结果可以区间缩小一半,最终锁定答案。

​ 二分法思路简单,但是根据查找需求有不同的模板,里面的细节需要注意。

算法模板

模板1:要找的数字一定存在时 而且唯一的时候

//二分查找 
#include <iostream>
using namespace std;
int binarySearch(int a[],int l,int r,int x)//递归
{
	if(l<=r)
	{
		int mid=(r+l)/2;
		if(a[mid]>x) return binarySearch(a,l,mid-1,x);
		if(a[mid]<x) return binarySearch(a,mid+1,r,x);
		if(a[mid]==x) 
		{
			return mid;
		}
	}
}
int binarySearch(int a[],int l,int r,int ch)//非递归
{
	int mid=(l+r)/2;
	while(l<=r)
    

		mid=(l+r)/2;
		if(a[mid]==ch)
        {
			return mid;
		}
        else if(a[mid]<ch)
        {
			l=mid+1;
		}else{
			r=mid-1;
		}
	}

}

int main(){
	int n,a[100],x;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	cin>>x;
	cout<<binarySearch(a,1,n,x);
	return 0;
}

模板2:要找的数字一定存在时 但是可能有多个时

这种情况下,我们需要查询的一般是元素第一次或者最后一次出现的位置

这时候 我们一般是二分到 区间从一个范围缩成 一个点

1.查 目标元素 第一次出现的位置

/*
测试数据1 输出 2  
5
1 3 3 3 9  
3
*/
//二分查找(返回第一次出现的位置 ) 
#include <iostream>
using namespace std;
int binarySearch(int a[], int l, int r, int x)
{
	int mid;
	while(l < r)
	{
		mid = (l + r ) / 2;
		if(a[mid] == x)//发现目标 在(l,mid]区间接着查询 
			r = mid;
		else if(a[mid] < x)
			l = mid+1;	
		else if(a[mid] > x)
			r = mid-1;
	}
	return l;
}
int main()
{
    int n, a[100], x;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    cin >> x;
    cout << binarySearch(a, 1, n, x);
    return 0;
}

2.查 目标元素 最后一次出现的位置

//二分查找(返回最后一次出现的位置 )
/*
测试数据 输出 4  
5
1 3 3 3 9  
3
*/ 
#include <iostream>
using namespace std;
/*
	这里需要注意 mid = (l + r + 1) / 2 这里
	当 l = 2 r = 3 且 a[l]==x 时 会陷入死循环 所以需要+1
	可以这么记:找到x元素后我们区间 mid 继续变大 
	mid变大就是让mid 右移  右移就需要+1
	
*/
int binarySearch(int a[], int l, int r, int x)
{
	int mid;
	while(l < r)
	{
		mid = (l + r + 1) / 2;// l = 2 r = 3会陷入死循环 需要 + 1 
		if(a[mid] == x)
			l = mid;
		else if(a[mid] < x)
			l = mid+1;	
		else if(a[mid] > x)
			r = mid-1;
	}
	return l;
}
int main()
{
    int n, a[100], x;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    cin >> x;
    cout << binarySearch(a, 1, n, x);
    return 0;
}

模板3:要找的数字可能存在 也可能有多个的时候

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXyMVRGG-1618979763577)(C:\Users\28934\AppData\Roaming\Typora\typora-user-images\image-20210421115448911.png)]

​ 这个时候 我们可以把数组大致分成三段: 小于目标数字的 等于目标数字的,大于目标数字的

1.当需要找 数字第一次的位置 或者 第一个大于目标数字的元素下标

​ 我们可以在红黄区域中间划线

在这里插入图片描述

​ 这样的话 区间被分成两段 红色区域(<) 和 黄绿色区域(>=)

​ 查第一次出现得位置 查找的数字不存在时 结果是第一个大于目标数字的元素下标

/*
测试数据1 输出 2 (查数字第一次出现的位置) 
5
1 3 3 3 9  
3

测试数据2 输出 4 (查的数字不存在 结果是第一个大于目标数字的元素下标) 
4
1 3 5 7
6

*/
//二分查找
#include <bits/stdc++.h>
using namespace std;
int n,a[100010],x,m;
//左边界(第一次出现得位置)查找的数字不存在 结果是第一个大于目标数字的元素下标 
int binarySearch(int a[],int l,int r,int x){
	int mid;
	while(l<r){
		mid=(l+r)/2;
		//是否是黄绿色区域 是的话从(l,mid)继续找
		if(a[mid]>=x){
			r=mid;
		}else{
			l=mid+1;
		}
	}
	return l;

}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	scanf("%d",&x);
	printf("%d ",binarySearch(a,1,n,x));
	return 0;
}

在这里插入图片描述

2. 当需要找 数字最后一次的位置 或者 最后一个小于目标数字的元素下标

​ 我们可以在黄绿区域中间划线

这样的话 区间被分成两段 红黄色区域(<=) 和 绿色区域(>)

/*
测试数据1 输出 4  查最后一次出现的位置 
5
1 3 3 3 9  
3

测试数据2 输出 3 查最后一个小于目标数字的 
4
1 3 5 7
6

*/
//二分查找(非递归) P2249
#include <bits/stdc++.h>
using namespace std;
int n,a[100010],x,m;
//右边界 (最后一次出现的位置)从左往右 查找的数字不存在 结果是最后一个小于目标数字的 元素下标 
int binarySearch(int a[],int l,int r,int x){
	int mid;
	while(l<r){
		mid=(l+r+1)/2;//l=2 r=3会陷入死循环 需要+1 
		//是否是红黄区域 是的话从(mid,r)继续找 
		if(a[mid]<=x){
			l=mid;
		}else{
			r=mid-1;
		}
	}
	return l;	
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	scanf("%d",&x);
	printf("%d ",binarySearch(a,1,n,x));
	return 0;
}

推荐习题

https://www.luogu.com.cn/problem/P2249 【深基13.例1】查找

https://www.luogu.com.cn/problem/P1678 P1678 烦恼的高考志愿

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值