目录

  • 二分查找的框架
  • 基本二分查找——寻找一个数
  • 寻找左侧边界的二分查找
  • 寻找右侧边界的二分查找

二分查找的思想就是通过判断中间值与目标值的大小来逐步缩短目标区间
将大规模问题转换为若干个子问题,解在其中一个子问题里

  • 查找算法
  • 数组有序或是有序的某种变体
  • 时间复杂度为O(logn)

二分查找的框架

public int binarySearch(int[] nums, int target) {int left = 0;int rigth = ...;while(...){int mid = right + (right - left) / 2;if(nums[mid] == target)...;else if(nums[mid] < target)left = ...;else if(nums[mid] > target)/right = ...;}return ...;}
  • 取中间数:mid = right + (right - left) / 2,而不是 (left + right) / 2,left + right可能大于整型范围
  • 使用else if 将所有情况都列出,展示所有细节
  • …为需要填入的变化的细节

基本二分查找——寻找一个数

这是最简单的二分查找,寻找一个数,存在则返回其索引,否则返回 -1

public int binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1; //细节1while(left <= right)         //细节2{int mid = right + (right - left) / 2;if(nums[mid] == target)return mid;         //细节3else if(nums[mid] < target)left = mid + 1;     //细节4else if(nums[mid] > target)/right = mid - 1;    //细节5}return -1;     //细节6}

细节1、2:为什么循环条件是left <= right 而不是left < right ?

因为初始化right的赋值为nums.length - 1, 而不是nums.length

这两者出现在功能不同的二分查找中,区别:前者搜索区间为[left, right],后者搜索区间为[left, right),因为nums.length角标越界

当nums[mid] == target 时,循环终止,若没找到该数,那么应该循环终止,返回 -1,终止的时候搜索区间应当为空,表示没有元素可以找了,就是没找到

while(left <= right)的终止条件是 left == right + 1,即[right + 1, right],这个区间是不存在的,即搜索区间为空, 这时直接返回 - 1是正确的

while(left < right)的终止条件是 left == right, [right, right],这时搜索区间非空,但循环终止了,这个区间被漏掉并没有搜索,这时直接返回 - 1就会出现错误

如果非要使用while(left < right),我们加个判断即可

return nums[left] == target ? left : -1;

细节4、5:为什么要 left = mid + 1,right = mid - 1?

当我们发现mid并不是要找的target, 那么当然是去搜索[left, mid - 1]或[mid + 1, right],mid我们已经判断过了, 不需要加入搜索区间了

但该算法无法得到target的左侧和右侧边界,向左向右线性查找会破坏二分的对数时间复杂度

寻找左侧边界的二分查找

public int binarySearch(int[] nums, int target) {int left = 0;int rigth = nums.length; //细节1while(left < right) //细节2{int mid = right + (right - left) / 2;if(nums[mid] == target)right == mid; //细节3else if(nums[mid] < target)left == mid + 1; //细节4else if(nums[mid] > target)/right == mid; //细节5}if(left == nums.length)return -1;return nums[left] == target ? left : -1; //细节6}

细节1、2:为什么循环条件是left < right 而不是left <= right ?

此代码的循环区间为[left, right), while(left < right) 终止的条件是 left == right,此时搜索区间 [left, left) 恰巧为空,所以可以正确终止

为什么right 初始赋值为nums.length?
请看细节6解析

细节3:为什么nums[mid] == target不直接返回而是right == mid ?
我们在找到target后不是立即返回,而是缩小right, 在区间[left, mid)中继续搜索,不断向左搜索达到锁定左侧边界的目的

细节4、5:为什么 left = mid + 1,right = mid ?,和之前不一样?
此代码的循环区间为[left, right),当mid被检测后应当分为[left, mid) 和[mid + 1, right)

细节6:为什么没有返回-1的操作,如果数组中没有target值怎么办
【左侧边界的理解】

对于上述数组,返回值为1,可以理解为:数组中小于2的元素有1个
如果target == 1, 返回值为0,数组中小于1的元素有0个
如果target == 5,返回值为4, 数组中小于5的元素有4个
那么return 的区间为[0, nums.length],我们添加return判断即可正确返回 - 1
这也就是为什么right 初始赋值为nums.length,因为我们有可能left == rigth == nums.length

while(...)
{...}
if(left == nums.length)return -1;
return nums[left] == target ? left : -1;

为什么返回left?
循环终止时left == right,返回哪个都行

寻找右侧边界的二分查找

整体思路与寻找左侧边界类似,只是有两处不同

 public int binarySearch(int[] nums, int target) {int left = 0;int rigth = nums.length; while(left < right){int mid = right + (right - left) / 2;if(nums[mid] == target)left == mid + 1; //细节1else if(nums[mid] < target)left == mid + 1; else if(nums[mid] > target)/right == mid; }if (left == 0) return -1;return nums[left-1] == target ? (left-1) : -1;; //细节2}

细节1 nums[mid] == target 时 left == mid + 1?
我们在找到target后不是立即返回,而是缩小left, 在区间[mid + 1, rigth)中继续搜索,不断向右搜索达到锁定右侧边界的目的

细节2为什么返回left - 1,而不是right?
while 循环的终止条件是 left == right,所以 left 和 right 是一样的,如果非要体现右侧的特点,返回 right - 1 即可
减一是右侧判断的特殊之处,因为我们对 left 的更新必须是 left = mid + 1,即mid = left - 1,就是说 while 循环结束时,nums[left] 一定不等于 target 了,而 nums[left - 1]可能是target。

不存在该值时也要进行判断
left 的取值范围是 [0, nums.length]
当left== 0时,0 是目标的右侧,因此该目标不存在

二分查找的魔鬼细节详解相关推荐

  1. java二分查找法_java算法之二分查找法的实例详解

    java算法之二分查找法的实例详解 原理 假定查找范围为一个有序数组(如升序排列),要从中查找某一元素,如果该元素在此数组中,则返回其索引,否则返回-1.通过数组长度可取出中间位置元素的索引,将其值与 ...

  2. python bisect_Python实现二分查找与bisect模块详解

    前言 其实Python 的列表(list)内部实现是一个数组,也就是一个线性表.在列表中查找元素可以使用 list.index()方法,其时间复杂度为O(n) .对于大数据量,则可以用二分查找进行优化 ...

  3. 满满干货:二分查找/排序 编程题详解

    铁汁们~今天给大家分享一篇有关二分查找/排序 编程题详解(牛客网),满满干货,来吧,开造⛳️ 先给大家说些小知识点: 1.指针变量名[整数]=*(指针变量名+整数): 2.知识点:双指针 双指针指的是 ...

  4. 二分查找 归并排序 快排 详解C++

    这三个排序算法一直是面试的重点,大多数都是C语言写的,今天整理了一下C++的写法,思想都差不多.这几个排序经常忘记,今天抽空记在这,以便自己以后查阅,不对的地方,也欢迎大家评论,不吝指正,谢谢! 二分 ...

  5. 二分查找之C#实现详解

    [前置知识] 二分查找是对有序数组(向量)而言的,所以在使用二分查找前要确保数组已经是顺序的(用到排序算法). [二分查找原理] 聚会时通常会玩猜数字的游戏: 先由坐庄的人来写一个数字(比如在1~10 ...

  6. linux shell 字符串操作(长度,查找,替换)详解 BASH

    linux shell 字符串操作(长度,查找,替换)详解 在做shell批处理程序时候,经常会涉及到字符串相关操作.有很多命令语句,如:awk,sed都可以做字符串各种操作. 其实shell内置一系 ...

  7. html的细节优化,网站页面优化细节详解

    原标题:网站页面优化细节详解 SEO页面优化是继SEO结构优化之后,另一个重要优化地方; 页面标题 在每个页面中的关键位置,出现目标关键词,这是我们做页面优化的基础思路,关键词位置,都有哪些呢? 第一 ...

  8. python避坑_Django搭建项目实战与避坑细节详解

    Django 开发项目是很快的,有多快?看完本篇文章,你就知道了. 安装 Django 前提条件:已安装 Python. Django 使用 pip 命令直接就可以安装: pip install dj ...

  9. Java 三目运算符细节详解

    Java 三目运算符细节详解 @author:Jingdai @date:2020.09.24 看到标题你肯定觉得三目运算符有什么好讲的,不是很简单嘛.我之前也是这么认为的,直到今天刷LeetCode ...

最新文章

  1. 对时序逻辑电路采用不同描述方式,ISE综合出来的电路(RTL Schematic)比较(以模5计数器为例)
  2. python中loop函数运用_使用涉及函数的Python在for循环中填充DataFrame
  3. Python开发Day03
  4. http反向代理之haproxy详解
  5. html弹出窗口加载别的页面地址,仿layer.open打开新窗口URL,自适应URL页面高度
  6. 卷积神经网络、比较MLPS和CNNS、滤波器、CNN各层的作用、在Pytorch可视化CNN
  7. Struts2内置拦截器的简要介绍
  8. tp5 linux路由不跳转,thinkphp5路由不生效一直跳到首页的解决方法
  9. sql语句基础学习(不涉及多表查询)
  10. 音频开发基本知识总结
  11. C程序设计--排序(冒泡、选择、插入)--选择
  12. Educoder Java面向对象 - 常用类
  13. 模拟集成电路设计基础知识(一):MOS管结构及其I/V特性
  14. 什么是单页应用SPA
  15. 资产泡沫即将湮灭! 转折全面到来!
  16. windows安装idea2019.3.3
  17. 三步为你的 Mac 设置黑客帝国矩阵屏保
  18. 商品管理系统SSM练习开发详细手册
  19. 用递归方法将十进制转化为二进制数
  20. 杭州烟花事故可能因燃放公司不熟环境-杭州-烟花事故

热门文章

  1. RabbitMQ之web界面解析
  2. Spring Boot 下 zookeeper搭配dubbo 服务注册与发现 搭建
  3. oracle系统中poord是什么,______A.tiredB.weakC.poorD.slow
  4. 记录一次云服务器被劫持下载了挖矿病毒的处理过程
  5. Mac App Store沙盒政策气走开发者 苹果“死不悔改”
  6. NLP-生成模型-2016-生成式摘要模型:Seq2Seq+Attention+Copy【Pointer网络的Copy机制解决Decoder端的OOV问题】【抽取式+生成式】【第一个生成式摘要模型】
  7. CAN整车网络学习-01
  8. 中国武术职业联赛(WMA)筹备活动报道--山东烟台
  9. Vue3.0 使用 ref 判断目标 node 区域之外的点击事件(实现下拉框、弹窗关闭功能)
  10. Tornado框架06-模板