二分法是在一个排好序的序列(数组,链表等)中,不断收缩区间来进行目标值查找的一种算法,下面我们就来探究二分法使用的一些细节,以及常用的场景:

  1. 寻找一个数;
  2. 寻找左侧边界;
  3. 寻找右侧边界。

一、二分法的通用框架

int binarySearch(vector<int>& nums, int target){
int left=0, right=nums.size();
while(left < right)
{
int mid=(left+right)/2;
if(nums[mid] == target){
// 条件一:中间的值与目标值相同
}
else if(nums[mid] > target){
// 条件二:中间的值大于目标值
}
else if(nums[mid] < target){
// 条件三:中间的值小于目标值
}
}
return -1;
}

首先,我们先来分析一下右边界 right 的初始值:

  1. right=nums.size() 时,初始化的区间就变成了 \([0, right-1]\),即 \([0,right)\);
  2. right=nums.size()-1 时,初始化的区间就变成了 \([0, right]\)。

在第一种情况下,当 nums[mid] > target 时,需要将区间向左收缩,即 right=mid。这个做法的逻辑是:既然 mid 位置处大于 target,而查找区间又是 “左闭右开”,因此当 right=mid 时,新的查找区间变成了 \([0, mid)\),这样才不会漏掉值。同理,当 nums[mid] < target 时,需要将区间向右收缩,即 left = mid+1,因为在 "左闭右开" 的区间下,新的查找区间变成 \([mid+1, right)\) 才不会漏掉值。当目标值不在序列中时,需要将 while 的条件写成 while(left < right) 而不是写成 while(left<=right),这样会引起数组越界。

第二种情况的分析类似,这里只给出结论:

  • nums[mid] > target 时,需要将区间向左收缩,即 right=mid-1
  • nums[mid] < target 时,需要将区间向右收缩,即 left = mid+1
  • 当目标值不在序列中时,需要将 while 的条件写成 while(left<=right)

二、二分法查找目标值

在序列中查找一个数,如果存在则返回数的索引,如果不存在则返回 -1 。为了方便分析,我们就只用第一种情况进行说明:

int binarySearch(vector<int>& nums, int target){
int left=0, right=nums.size();
while(left < right)
{
int mid=(left+right)/2;
if(nums[mid] == target){
return mid; // 查询到目标值,直接返回目标值的位置
}
else if(nums[mid] > target){
right = mid; // 中间的值大于目标值,向左收缩区间
}
else if(nums[mid] < target){
left = mid+1;// 中间的值小于目标值,向右收缩区间
}
}
return -1; // 当没有找到,直接返回-1
}

三、二分法查找目标值的左右边界

上述代码只能从序列中查找一个目标值并返回位置,当一个序列中目标值不止一个时,我们需要找到目标值最左边的位置和最右边的位置,这时候二分法需要进行改写:

// 查找目标值的左边界
int binarySearch(vector<int>& nums, int target){
int left=0, right=nums.size();
while(left < right)
{
int mid=(left+right)/2;
if(nums[mid] == target){
right = mid; // 查询到目标值不进行返回,而是收缩区间继续查找
}
else if(nums[mid] > target){
right = mid; // 中间的值大于目标值,向左收缩区间
}
else if(nums[mid] < target){
left = mid+1;// 中间的值小于目标值,向右收缩区间
}
}
return left;
}

根据上述代码,可以发现如果查找目标值的左边界,在满足 nums[mid] == target 时,需要缩小搜索区间的上界 right,在区间 \([left, mid]\) 中继续搜索,直到搜索完毕 left==right。此时 left=right=左边界

查找右边界的做法与左边界类似:

// 查找目标值的左边界
int binarySearch(vector<int>& nums, int target){
int left=0, right=nums.size();
while(left < right)
{
int mid=(left+right)/2;
if(nums[mid] == target){
left = mid+1; // 查询到目标值不进行返回,而是收缩区间继续查找
}
else if(nums[mid] > target){
right = mid; // 中间的值大于目标值,向左收缩区间
}
else if(nums[mid] < target){
left = mid+1;// 中间的值小于目标值,向右收缩区间
}
}
return left-1;
}

注意这里的判断条件改成了当 nums[mid] == target 时,left = mid+1。因为搜索的区间为 "左闭右开",所以在寻找左边界时可令 right=mid ,在寻找右边界时必须另 left=mid+1,不然程序会一直停在循环里面而无法跳出循环。

C++实现二分法详解的更多相关文章

  1. Python中的高级数据结构详解

    这篇文章主要介绍了Python中的高级数据结构详解,本文讲解了Collection.Array.Heapq.Bisect.Weakref.Copy以及Pprint这些数据结构的用法,需要的朋友可以参考 ...

  2. javascript 中合并排序算法 详解

    javascript 中合并排序算法 详解 我会通过程序的执行过程来给大家合并排序是如何排序的...  合并排序代码如下: <script type="text/javascript& ...

  3. 深入MySQL用户自定义变量:使用详解及其使用场景案例

    一.前言 在前段工作中,曾几次收到超级话题积分漏记的用户反馈.通过源码的阅读分析后,发现问题出在高并发分布式场景下的计数器上.计数器的值会影响用户当前行为所获得积分的大小.比如,当用户在某超级话题下连 ...

  4. SQL注入漏洞技术的详解

    SQL注入漏洞详解 目录 SQL注入的分类 判断是否存在SQL注入 一:Boolean盲注 二:union 注入 三:文件读写 四:报错注入 floor报错注入 ExtractValue报错注入 Up ...

  5. 二分算法题目训练(四)——Robin Hood详解

    codeforces672D——Robin Hood详解 Robin Hood 问题描述(google翻译) 我们都知道罗宾汉令人印象深刻的故事.罗宾汉利用他的射箭技巧和他的智慧从富人那里偷钱,然后把 ...

  6. 线段树详解 (原理,实现与应用)(转载自:http://blog.csdn.net/zearot/article/details/48299459)

    原文地址:http://blog.csdn.net/zearot/article/details/48299459(如有侵权,请联系博主,立即删除.) 线段树详解    By 岩之痕 目录: 一:综述 ...

  7. 丰富图文详解B-树原理,从此面试再也不慌

    本文始发于个人公众号:TechFlow,原创不易,求个关注 本篇原计划在上周五发布,由于太过硬核所以才拖到了这周五.我相信大家应该能从标题当中体会到这个硬核. 周五的专题是大数据和分布式,我最初的打算 ...

  8. RocketMQ源码详解 | Broker篇 · 其三:CommitLog、索引、消费队列

    概述 上一章中,已经介绍了 Broker 的文件系统的各个层次与部分细节,本章将继续了解在逻辑存储层的三个文件 CommitLog.IndexFile.ConsumerQueue 的一些细节.文章最后 ...

  9. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

随机推荐

  1. 【转载】geany linux python编译器 开源

    http://www.dekiru.cn/?p=1491 Geany 不好用,建议用一些好用的编辑器或ide Subliem Text 或 VS code Pycharm等. 设置运行环境 菜单栏–生 ...

  2. -bash: ll: 未找到命令

    第一步将alias ll='ls -l'添加到/etc/profile # head /etc/profile# /etc/profilealias ll='ls -l'# System wide e ...

  3. WPS-插入-公式-菜单 怎样在EXCEL中使用PRODUCT函数

    怎样在EXCEL中使用PRODUCT函数 ################   WPS2018 -插入-公式-[专门有公式菜单] 插入函数       ##################       ...

  4. MySQL给某个用户给某个库表设置权限

    -- 用root(最高权限的用户)进行以下操作-- 创建数据库:emc_power CREATE DATABASE emc_power DEFAULT CHARACTER SET utf8 COLLA ...

  5. mysql有关配置

    mysql有关配置 mysql安装 mysql安装方式有三种 源代码:编译安装 二进制格式的程序包:展开至特定路径,并经过简单配置后即可使用 程序包管理器管理的程序包: rpm:有两种 OS Vend ...

  6. 上,打开SSH服务的配置文件:/etc/ssh/sshd_config 加上如下两行: ClientAliveInterval 120 ClientAliveCountMax 720 第一行,表示每隔120秒向客户端

    SSH的默认过一段时间会超时,有时候正在执行着脚本,出去一会回来就断开了,输出信息都看不到了... 禁止SSH自动超时最简单的办法就是,每隔一段时间在客户端和服务器之间发送一个"空包&quo ...

  7. 在Linux服务器,搭建K8s服务【脚本篇】

    前言 好久没有写博客了,本文主要是对网上文章的总结篇,主要是将安装和运行代码做了一次真机实验,亲测可用.文章内包含的脚本和代码,多来自于网络,也有我自己的调整和配置,文章末尾对参考的文献做了列举,方便 ...

  8. GO学习-(14) Go语言基础之接口

    Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类 ...

  9. Jmeter- 笔记9 - CLI(无图形界面)

    使用CLI模式,减少资源占用 用GUI调试好脚本 在jmeter的bin文件夹运行cmd,然后输入命令:jmeter -n -t [jmx file] -l [results file] -e -o ...

  10. 3层-CNN卷积神经网络预测MNIST数字

    3层-CNN卷积神经网络预测MNIST数字 本文创建一个简单的三层卷积网络来预测 MNIST 数字.这个深层网络由两个带有 ReLU 和 maxpool 的卷积层以及两个全连接层组成. MNIST 由 ...