二分法总结(超级详细)附带图解-程序员宅基地

技术标签: 算法  蓝桥杯刷题训练  数据结构  

1. 二分法

二分查找是一个时间效率极高的算法,尤其是面对大量的数据时,其查找效率是极高,时间复杂度是log(n)。
主要思想就是不断的对半折叠,每次查找都能除去一半的数据量,直到最后将所有不符合条件的结果都去除,只剩下一个符合条件的结果。

2. 时间复杂度:

  二分法的时间复杂度是log(n),但log(n)为什么效率这么高呢?接下来我举个例子来描述一下:

  我们都听说过指数爆炸,何为指数爆炸,就是在指数不断增加的情况下,其数值的上升速度不断增加,上升速率可以迅速的接近增无穷。

如图:
在这里插入图片描述

同样也有这样一个理论去描述指数爆炸的威力:那就是一个乒乓球每秒钟翻一倍,五分钟可以填满整个宇宙。

而我们二分法就相当于上升的逆转,一个每秒钟翻一倍的乒乓球,五分钟就可以堆满整个宇宙,反之,堆满整个宇宙的乒乓球,每秒钟减少一半,五分钟后就只剩一个。

3. 二分法的套路

使用二分法,我们要按照一下两个步骤:

  1. 我们要确定一个区间[L,R]
  2. 我们要找到一个性质,并且该性质满足一下两点:
    ①满足二段性
    ②答案是二段性的分界点。

这里可能很多人有疑问了,这性质到底是什么性质,其实就是根据题目找出的判断条件。

例如:

我们要在一组升序的数组找一个数的下标,那我们肯定是先拿中间的与他进行比较,更具比较大小这个判断,其实就相当于是这个性质,且这个性质满足二段性,将大于和小于我们要查找的值分为两段,而我们的查找结果就是分界点

3.1 整数的二分

情况一:
当ans属于左边界时,如图进行划分:
在这里插入图片描述
这种情况有两个区间[L,mid1]和[mid1 + 1,R],我们要根据条件将某一半区间舍去,而为什么要这样划分呢?
根据图分析:

  1. 当mid属于红色区域时,也就是mid1,我们发现,mid1是有可能等于ans的,为了避免我们将ans排除在区间外,我们令L = mid,从而在除去左边不需要的数据同时,好保证了ans任在区间内。
  2. 当mid属于蓝色区域,及mid2时,mid2是不会等于ans的,所以我们可以将包括mid2的右边区间全部去除,及令R = mid - 1。

模板1如下:

while(l < r){
    
	int mid = l + r + 1 >> 2;//这里为什么要+1呢,请继续往下看
	if(在红色区域内)l = mid;
	else r = mid - 1;
}

我们取个临界情况,及当L = R - 1时,
我们知道,在上面这种情况下,是进行L = mid进行区间调整的,假设mid = (L + R) / 2,那么L = (L + R) / 2 = (2 * R - 1) / 2 = L(很明显的奇数除以二,会进行向下取整),所以这样会造成死循环。

情况二:
在这里插入图片描述
当ans属于右边界时:
这种情况划分为[L,mid - 1]和[mid,R],因为当mid在蓝色区域(mid2)时,mid2可能等于ans。同样的分析方式,可以自己分析一下(加深理解)

模板2如下:

while(l < r){
    
	int mid = l + r >> 1;//左移1有除2的效果,+的优先级大于>>
	if(mid 属于蓝色区域) r = mid;
	else l = mid + 1;
}

3.2 实数的划分

实数的划分相对与整数要简单,没有这么多种情况,因为实数除以2的结果不会有什么向上或向下取整的情况,一定会有个原原本本的结果,就L = mid,R = mid这种区间转变的方式,而循环条件通常是L - R > 1e-6,1e的负次方根据题目进行调整。

模板:

while(l - r > 1e-6){
    
	if(arr[mid] > ans)l = mid;
	else r = mid;
}

四. 相关习题

实践才是检验整理的唯一标准,上面讲了这么多理论,刚开始接触的人可能还是会有点蒙,接下来我们看几个例子来真真切切的感受一下:

4.1 数的范围

题目链接: 789. 数的范围

这道题是一道模板题,并且我觉得很好,因为他把整数二分的两种模板都用上了。

题目要求:

题目要求是,给我们长度为n的升序数组和一个数q,q表示查询的次数,每次查询给一个值x,要求找出x的区间,比如1,2,2,3,4,4,4,如果x的4,那么我们的输出结果就是4 6,因为含有4的下标区间在[4,6]内。

思路分析:

  1. 首先我们要获得判断确定区间,很明显区间是[0, n - 1]。
  2. 寻找性质。
    我们先考虑如何寻找x的左边界,很明显处在左边界的值一定是>=x,
    如图:

在这里插入图片描述
同时也刚好将区间分为两端,符合二段性,且是分界点。
我们继续分析:
因为ansL在蓝色范围内,所以我们应该进行如下转变方式:L = mid + 1,R = mid。

左边界代码如下:

while(l < r){
    
	int mid = l + r >> 1;
	if(a[mid] >= x)r = mid;
	else l = mid + 1;
}
//找出之后我们还要判断,a[r]是否等于x,如果不等于则说明没有x,输出-1 -1
if(a[r] != x)cout << "-1 -1" << endl;
else{
    //否则我们寻找r
	;
}

现在我们分析右边界
左边界是一定大于等于x,那么我们的右边界显然是判断小于等于x。
在这里插入图片描述
这时我们进行如下调换,L = mid,R = mid - 1,且mid = L + R + 1 >> 1。

右边界代码如下:

while(l < r){
    
	int mid = l + r + 1 >> 1;
	if(a[mid] <= x)l = mid;
	else r = mid - 1;
}

完整代码:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int n, q;
int a[N];

int main(){
    
    cin >> n >> q;
    for(int i = 0; i < n; i++)scanf("%d", &a[i]);
    while(q -- ){
    
        int x;
        scanf("%d", &x);
        int l = 0, r = n - 1;
        //首先找到左区间的下标
        while(l < r){
    
            int mid = l + r >> 1;
            if(a[mid] >= x)r = mid;
            else l = mid + 1;
        }
        //判断a[l] == x
        if(a[r] != x)cout << "-1 -1" << endl;
        else {
    
            cout << l << ' ';//先将l输出
            r = n - 1;//重置r
            //查找ansL
            while(l < r){
    
                int mid = l + r + 1 >> 1;
                if(a[mid] <= x)l = mid;
                else r = mid - 1;
            }
            cout << r << endl;
        }
    }
    return 0;
}

4.2 数的三次方根

题目链接: 数的三次方根

题目分析:

这是一道实数二分的模板题,给定一个数,让我们求它的三次方根,精确到小数点后6位

思路分析:

首先题目给定的范围时-10000到10000,同时也是区间范围。
在这个区间范围内我们找一个数x,使得x * x * x等于n,则x就是n的三次方根,且x刚好是二段性的分界点,如果mid ^ 3>=n说明mid过大,则R = mid,否则L = mid。

循环条件: R - L > 1e-8
为了保证精度够高,我们通常将范围缩小到比题目要求低二次方。
在这里插入图片描述

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
int main(){
    
    double n;
    cin >> n;
    double l = -10000, r = 10000;
    while(r - l > 1e-8){
    
        double mid = (l + r) / 2;
        if(mid * mid * mid < n)l = mid;
        else r = mid;
    }
    printf("%.6lf\n", l);
    return 0;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_53060585/article/details/122735971

智能推荐

TimeGen 软件的使用_timegen 字体太小-程序员宅基地

文章浏览阅读1.1k次。官网 http://www.xfusionsoftware.com/timegen 是一款实用的画时序图工具,软件提供了直观的用户界面和丰富实用的绘图工具,可以帮助用户轻松绘制各种序列图、顺序图、循序图等,同时timegen还拥有实用的快捷键操作功能,能够让你绘图时序图更加轻松,且可以自由设置各个文本框的属性字体样式、字体 大小和颜色等。下面简单介绍一下他的应用:主要参考:https://blog.csdn.net/qq_25144391/article/details/104423988?ops__timegen 字体太小

adb shell uiautomator dump /doinf/uidumpa.xml 一切正常,就是没有显示这个文件。_adb shell uiautomator dump无效-程序员宅基地

文章浏览阅读1.2k次。返回的UI hierchary dumped to: /doinf/uidumpa.xml但是手机里就是没有这个文件。这是什么情况啊?_adb shell uiautomator dump无效

kaldi中声纹识别ivector模型_kaldi i-vector-程序员宅基地

文章浏览阅读4.4k次,点赞2次,收藏24次。1.数据准备:无论使用kaldi来做语音识别还是说话人识别,第一步就是数据准备,对于说话人识别来说,需要准备的几个文件为wav.scp,utt2spk,spk2utt这三个文件。对应的格式如下: 1.1 wav.scp有两列,第一列是key,这个可以一定要唯一;第二列是 wav的路径wavpath; 1.2 utt2spk也有两列,第一列是key,与wav.scp的第一列一样;..._kaldi i-vector

2019-程序员宅基地

文章浏览阅读4.2k次。序言2019年好像没几天就要结束了,所以写个渣渣凑个数量,爱看的看看,不爱看的滑过。2019是猪队友的元年,所以总结为2个字就是炮灰。风言风语1 猪队友你..._office2019专业增强版激活

Git(五)常用命令精简整理_git bash命令-程序员宅基地

文章浏览阅读896次。全局设置git config --global user.name "acgkaka"git config --global user.email "[email protected]"初始化.git文件夹git init将当前文件夹连接到test远程仓库git remote add origin https://gitee.com/acgkaka/test.git将本地的当前分支推送到远程的master分支,同时指定origin为默认主机,(后面再使用git push的时候就可以不加任_git bash命令

spring boot2.0自定义注入mongoTemplate使用审计标签@EnableMongoAuditing报错_springboot2.0+mongotemplate-程序员宅基地

文章浏览阅读6k次。项目原来在spring boot1.5.9版本时候使用@EnableMongoAuditing用同样的方法注入并没有报错,当切换到2.0版本是莫名其妙的出问题了,搞的我一脸懵逼,花了好久都没解决,后来偶然看到我们公司一个大佬的自定义注入的的方式,瞬间感觉到了王者和青铜的差距。 下面是配置代码@Configuration@EnableMongoAuditing@PropertySourc..._springboot2.0+mongotemplate

随便推点

分治算法思想及应用_分治思想-程序员宅基地

文章浏览阅读3.8k次,点赞5次,收藏22次。一. 分治算法介绍1. 分治算法思想2. 分治算法适用条件3. 分治算法的引入二. 分治算法的应用1. 快速排序2. 快排划分函数求topk问题3. 归并排序4. 合并k个有序单链表5. 对数时间求中位数算法思想_分治思想

Linux|minio对象存储服务的部署和初步使用总结_linux部署minio-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏5次。minio是一个非常轻量化的对象存储服务,是可以算到云原生领域的。该服务是使用go语言编写的,因此,主文件就一个文件,它的下载,部署什么的都是非常简单的,一般两三步就可以搭建好了,只是有一些细节问题需要在部署使用的时候注意。本文将就一个可用的minio存储服务部署做一个尽量详细的讲解,并探讨如何将该技术落地_linux部署minio

MATLAB2018a与VS2015 C++编译包安装下载的心路历程与解决之道_matlab安装vs2015编译器-程序员宅基地

文章浏览阅读1.9k次,点赞2次,收藏12次。前言:本着前人栽树后人乘凉的精神。感谢csdn朋友们所分享出来关于如何解决的安装方法,以我的下载安装的成功的经验来为困扰各位奉献一点力量。_matlab安装vs2015编译器

PL2586/USB2.0HUB工业级多口集线器扩展芯片|MA8601升级版_usb 扩展 芯片-程序员宅基地

文章浏览阅读355次。PL2586是旺玖新出的一款USB HUB 芯片PL2586是一项创新,它集成了符合USB-IF“电池充电规范修订版1.2”的功能,支持便携式设备的快速充电功能。此功能将PL2586转变为“通用充电解决方案”(UCS)兼容的基于电池的便携式设备的USB充电集线器,由GSMA推广。当在下游端口检测到符合B.C.标准的便携式设备时,PL2586中的专用端口可以处理充电请求。而且,在握手完成后,PL2586允许便携式设备达到900mA(高速);1.5A(低速/全速)来自充电下游端口(CDP)或1.5A来自_usb 扩展 芯片

本周直播预告|OCR打卡营12月21日(周二)起每晚八点半正式开讲!-程序员宅基地

文章浏览阅读109次。直播课表12月21日(周二)正式开讲!课程报名地址https://aistudio.baidu.com/aistudio/course/introduce/25207内容概览OCR方向万星...

管理心理--程序员如何选择职业赛道-程序员宅基地

文章浏览阅读2.1k次,点赞81次,收藏22次。当然,除非你创业,不然做不同类型的系统,对一个程序员来说,没啥不同,我带过的同学,有很多做着搜索引擎,突然转到游戏引擎,或其它基础架构组件的。一是了解软件运行的原理,知道系统薄弱点在哪,比如曾经一个下属在转为测试后,测试系统稳定性,必有一项:就是拿乱码去测试,看系统是否还能正常稳定地运行。很多系统将自己的稳定性,交给别人请求的合法性,这是不对的。但有编码经验的同学,在做运维实施,尤其是在客户现场,做一些私有化的安装部署时,面对系统的日志、模块的日志、配置,能更快、更专业地把问题定位出来。

推荐文章

热门文章

相关标签