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

  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. RHEL_高级磁盘管理(vdo、stratis)

    RHEL高级磁盘管理-VDO VDO简介 Virtual Data Optimizer 通过数据去重.压缩的方式来优化存储空间. VDO层放置在现有块存储设备上,例如Raid设备.本地磁盘设备. LV ...

  2. 运维实战案例之“Too many open files”错误与解决方法

    运维实战案例之"Too many open files"错误与解决方法   技术小甜 2017-11-16 15:02:00 浏览869 服务器 shell tomcat 脚本 o ...

  3. 1.1Ubuntu安装

    在虚拟机中安装 Ubuntu 步骤 安装前的准备和基本安装 设置语言环境 安装常用软件 1. 安装前的准备和基本安装 1.1 安装前的准备 访问 http://cn.ubuntu.com/downlo ...

  4. Linux 用户管理_用户相关配置文件详解

    linux的用户管理 linux支持多个用户同时使用同一个用户登陆系统,windows在修改组策略的情况下,也可以多个人使用同一个用户登陆. 远程连接Linux的方式:ssh 远程连接windows的 ...

  5. Flink-cdc实时读postgresql

    由于公司业务需要,需要实时同步pgsql数据,我们选择使用flink-cdc方式进行 架构图: 前提步骤: 1,更改配置文件postgresql.conf # 更改wal日志方式为logicalwal ...

  6. ubuntu 14.04安装mysql-python

    网上看到的是想安装mysql-python都得安装mysql本身,可是我就不想安装这个数据库,而是用于连接到别的服务器上的mysql,所以下面就是安装过程: 1. 直接运行: pip install ...

  7. 『动善时』JMeter基础 — 30、JMeter中JSON断言详解

    目录 1.JSON断言组件界面详解 2.JSON断言组件的使用 (1)测试计划内包含的元件 (2)登陆接口请求界面内容 (3)JSON断言界面内容 (4)查看运行结果 (5)断言结果组件说明 3.JS ...

  8. GO学习-(32) Go实现日志收集系统1

    Go实现日志收集系统1 项目背景 每个系统都有日志,当系统出现问题时,需要通过日志解决问题 当系统机器比较少时,登陆到服务器上查看即可满足 当系统机器规模巨大,登陆到机器上查看几乎不现实 当然即使是机 ...

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

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

  10. Python+Selenium学习笔记19 - 自动发送邮件

    发送简单的邮件 用一个QQ邮箱发送到另一个QQ邮件. 首先设置QQ邮箱,邮箱设置 -> 账号 开启SMTP服务,点击开启按钮,按提示进行操作,需要1毛钱的短信费.开启后如下所示 1 # codi ...