算法-基础算法-二分

二分算法详解与应用

算法-基础算法-二分

原理

对于一个数列(arr)来说,如果其具有单调性(单调递增,单调递减),那么可以利用这种单调性来快速的查找一个目标数字(具体过程如下):
1:
因为具有单调性,所以对于数列中的任意一个数字x,必然可以将数列分为大于x,和小于等于x两部分。
2:
利用上述性质,我们每次取当前目标数列(最开始是arr本身,后面可能是从下标l开始到r结束的arr的一部分)的下标的中间值(下标为(l+r)/2 ; (l+r+1)/2 ))所对应的值mid
3:
每次用mid对应的值和查找的目标值x比较,如果mid>x就将目标数列更新为从下标从l=l开始到r=mid(等号右边是新的值,l=l代表与上一次一致)因为目标值一定在更新后的数列里边,这样我们就缩小了范围,一直缩小直到l==r,这时候l和r对应的就是x(目标值)-----这里讲解的事单调递增的情况

整数二分

整数二分针对的是离散的数据,比如说一个数列3,1,2,4,6就是离散的(但这个数列不具有单调性)
关键点
1.关注边界条件
取等于条件的是符合条件的,可以进行单独讨论
不取等于条件的时候需要+1(l)-1®
2.合法性检查
检查最后的结果是否符合要求
3.两种情况的讨论
要找==“第一个 ≥x”的位置,mid = (l + r) / 2 是向下取整,是安全的;
但如果要找
“最后一个 ≤x”的位置,应该用 mid = (l + r + 1) / 2 防止死循环。(也就是说如果符合条件更新的是l==就需要用(l+r+1)/2,更新r用(l+r)/2)

模板1:找第一个 ≥x 的位置

const int N=1e5+10;
int arr[N];//假设是递增序列
int n;
int search(int x)
{
	int l=0,r=n,mid=0;
	while(l<r)//l到r的开区间
	{
		mid=(l+r)/2;//边界条件
		if(arr[mid]>=x)
		{
			r=mid;
		}
		else//小于的情况,不应该包含mid所在
		{
			l=mid+1;
		}
	}
	if(x==arr[r])return r;
	return -1;//没找到 
}

py代码

# 整数
arr=[]
x=int(input("请输入一个整数:"))
def binary_search_leftmost(arr, x):
    low, high = 0, len(arr) - 1
    while low < high:
        mid = (low + high) // 2
        if arr[mid] >= x:
            high = mid
        else:
            low = mid + 1
    return low if arr[low] == x else -1

实数二分

实数针对的是一个区间,比如说[1,5],要寻找其上满足某函数的一个数,下面以x的开根号为例

模版二y=sqrt(x)

int sqrt_search(double x,double accur,double l,double r)
{
	double mid=0;
	while((r-l)>accur)
	{
		double mid =(l+r)/2;
		if(mid*mid<n)l=mid;
		else r=mid
	}
return mid;
}

py

#实数二分
def bin_search_sqrt(x):
    low=0
    high=x
    mid=(low+high)/2
    while abs(mid**2-x)>1e-6:
        if mid**2<x:
            low=mid
        else:
            high=mid
        mid=(low+high)/2
    return mid

答案二分

关键:
check函数
通过观察我们发现,每个二分都存在一个判断的条件来调节l,r。
check函数是针对mid的函数,就像整数二分的==>==号。
check的作用是查看当前元素是否满足某个性质,而查找的目标元素一定是满足这个性质(check函数)的边界元素

二分答案oiwiki------>>>从答案可能的取值出发考虑问题,而不是进行过程的模拟

解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。若满足单调性,则满足使用二分法的条件。把这里的枚举换成二分,就变成了「二分答案」。

注意类似最大的最小值等问题

枚举:枚举所有答案可能的取值,直到符合条件的取值---------->二分:将可能的答案区间进行二分,通过判断mid来确定范围

例题:
蓝桥云课肖恩的苹果树

#include<bits/stdc++.h>
using namespace std;
int n,m;
bool check(int x,int a[] ){
    int last=0,current=1;
    int res=1;
    for(;current<n;current++){
        if(a[current]-a[last]>=x){
            last=current;
            if(++res==m)return true;
        }
        else{
            ;
        }
    }
    return false;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    int a[n];
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    sort(a, a + n);
    int l=1;
    int r=a[n-1]-a[0];
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid,a))l=mid;
        else r=mid-1;
    }
    cout<<l<<endl;
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值