二分法


前言

复习acwing算法基础课的内容,本篇为讲解基础算法:二分,关于二分查找法的时间复杂度的分析,以后会补充到本博客中,目前先鸽了,对于题目的代码不做过多解释,二分查找中每一行代码的详细见二分查找的模板


一、二分查找法[整数]

1.例题:AcWing 789. 数的范围

这里结合具体的习题去讲解二分,并且给出代码模板:二分例题的链接:AcWing789.数的范围
本博客给出本习题的截图:在这里插入图片描述

2.本题代码:

代码如下:

(1)本题核心部分详解:

/*
	写之前明确自己要求什么
	假设查找x的坐标,本题输出中的第一个数为≥x的最小位置,第二个输出为≤x的最大位置
	如果没有该元素就输出“-1 -1”
	对于查找≥x的最小位置:
*/
	int l = 0, r = n - 1; //题意从0开始为起始坐标,故终点坐标为n - 1
	while(l < r)
	{
		int mid = l + r >> 1 	  //等同于 mid = (l + r) / 2;
		if (q[mid] >= x)          //q[mid]就是二分后的最中间的数
							      //q[mid]如果≥x,我们要找的是≥x的最小位置
							      //即我们要找的位置在mid的左边或就是mid(当只有一个数的时候)
			r = mid;		      //故我们要更新r,使得r = mid;
		else 				      //即q[mid] < x,而我们要找的是≥x的数,即要找的在mid的右边
			l = mid + 1;          //故更新l为mid + 1
	}
/*
	此时我们的l,或者是r就是我们要找的起始坐标
	输出起始坐标还有一个问题:如何保证我们真正的找到了这个数
*/
	if (q[l] != x)  printf("-1 -1");
	//如果我们真正的找到了这个数,那么我们的q[l]或是q[r]应该是等于我们要查找的数字x的
	//所以有该if语句,即如果不等于的话,就输出"-1 -1"

	//接下来去查找≤x的最小位置:
	int l1 = 0, r1 = n - 1//这里的l1我们可以从0开始,也可以从l开始
	while (l < r)
	{
		int mid = l + r + 1 >> 1;  //等同于 mid = (l + r + 1) / 2
		//至于这里为什么要写成(l + r + 1) / 2,见代码模板
		if (q[mid] <= x)		   //q[mid]如果≤x,我们要找的是≤x的最大位置
								   //即我们要找的位置在mid的右边或就是mid(当只有一个数的时候)
			l = mid;			   //故我们要更新l,使得l = mid;
		else                       //即q[mid] > x,而我们要找的是≤x的数,即在mid的左边
			r = mid - 1;           //故更新r为mid - 1
	 }

(2)AC代码:

#include <iostream>

using namespace std;

const int N = 100010;

int q[N];

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 0; i < n; i ++ ) cin >> q[i];
    
    while (m -- )
    {
        int k;
        cin >> k;
        
        int l = 0, r = n - 1;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (q[mid] >= k) r = mid;
            else l = mid + 1;
        }
        
        if (q[l] != k) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' ';
            
            int l1 = 0, r1 = n - 1;
            while (l1 < r1)
            {
                int mid = l1 + r1 + 1 >> 1;
                if (q[mid] <= k) l1 = mid;
                else r1 = mid - 1;
            }
            
            cout << r1 << endl;
        }
    }
    
    return 0;
}

二、二分查找法[浮点数]

1.例题:AcWing 790. 数的三次方根

例题连接:数的三次方根
本博客给出本习题的截图:在这里插入图片描述

2.AC代码:

代码如下:

#include <iostream>

using namespace std;

int main()
{
    double x;
    cin >> x;
    
    double l = -10000, r = 10000;   //因为要通过二分去求,左右端点直接赋值成题目中给定的n的范围边界
    while (r - l > 1e-8)            //当所求l和r十分接近的时候,即可求出³√x的值
    //题目中要求保留6位小数,故这里写成1e-8最为保险(高题目要求2个精度)
    //即题目若要求四位小数的话,写成1e-6,以此类推
    {
        double mid = (r + l) / 2;
        if (mid * mid * mid >= x) r = mid;    //证明³√x ≤ mid,即所求在mid左边,故 r = mid
        else l = mid;
    }
    
    printf("%.6lf", l);
    
    return 0;
}

三、二分查找的代码模板:

本模板来自AcWing的y总-算法基础课,AcWing链接:AcWing

1.整数二分:

bool check(int x) {/* ... */} // 检查x是否满足某种性质
//以下为y总总结的两个代码模板,可选择任何一个进行记忆,注意区别两个代码的不同
//背下下来拿任何一个就足够了;
int bsearch_1(int l, int r) 
{
    while (l < r)                   
    {
        int mid = l + r >> 1;		//等同于  mid = (l + r) / 2;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;        
        if (check(mid)) l = mid;    
        //如果更新为 l = mid,则上一行需要补上+  1,即mid = l +  r + 1 >> 1
        //如果更新如 bsearch_1 中的r = mid则不需要补 + 1
        //具体原因y总上课有将,没有太理解什么意思,索性不求甚解
        //这里可以先记住 是否补充 + 1取决于更新后是r = mid还是l = mid,后者需要补 + 1
        //具体为什么需要补充 + 1,后续理解了会更新说明。
        else r = mid - 1;
    }
    return l;
}

2.浮点数二分模板:

bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
    const double eps = 1e-8;   // eps 表示精度,取决于题目对精度的要求(要比题目要求的高2个精度)
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

四、时间复杂度的分析:

博主自己还没搞懂,先鸽了,日后一定补齐时间复杂度和证明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_52156445

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值