1.二分查找是什么?

二分查找也称折半查找,是一种效率较高的查找方式。但是,二分查找要求线性表为顺序存储结构且表按关键字有序排列。
时间复杂度O(log2n)

2.二分查找的思路分析

便于叙述,笔者以数组array[N] = [1,10,20,30,99,1020]为例。left、right分别表示数组的左界和右界。需要查找的值记为value。

  1. 首先,我们需要确定数组中间的下标(位置):mid = (left+right)/2;
  2. 将需要查找的数value与array[mid]比较:
    (1)若value > array[mid]:说明需要查找的数在mid的右边,需要更新left、mid值向右侧查找;
    (2)若value < array[mid]: 说明需要查找的数在mid的左边,需要更新right、mid值向左侧查找;
    (3)若value = array[mid]: 说明找到了需要查找的数,返回位置值即可。

3.思路图解

假设我们需要查找数值99,mid首先指向的位置应为20的位置。left指向1,right指向1020。判断array[mid] = 20 与 99 不相等,此时,left = mid + 1,右移;right 不变;mid更新为4。此时,array[mid] = 99, 查找成功,返回索引值4。

4.代码实现(递归方式)

何时递归结束???

  • 找到value,返回mid;
  • 没有找到value:此时整个数组已经被递归搜索完成,left > right,结束递归,并且返回-1(认为没有匹配值)。
    具体Java代码实现如下:
public class BinarySearch {public static void main(String[] args) {int[] array = {1,10,20,30,99,1020};// 待查找值设置为99 、 0int value1 = 99;int value2 = 0;System.out.println(value1 + "'s index: " + bs(array,0,array.length-1,value1));System.out.println(value2 + "'s index: " + bs(array,0,array.length-1,value2));}// 二分查找递归实现/**** @param array 数组* @param left 左端点* @param right 右端点* @param value 待查找的值* @return 查找到value则返回索引值;否则返回-1*/public static int bs(int[] array, int left, int right, int value) {// 没有找到if(left > right){return -1;}int mid = (left + right) / 2;if(value > array[mid]){// 向右查找,更新left = mid + 1return bs(array, mid + 1, right, value);}else if(value < array[mid]){// 向左查找,更新right = mid - 1return bs(array,  left, mid - 1, value);}else {return mid;}}
}

5.代码实现(非递归方式)

非递归的方法体如下,具体见注释:

/*** * @param array 数组* @param value 待查找的值* @return 查找到value则返回索引值;否则返回-1*/public static int binarySearch(int[] array, int value){int left = 0;int right = array.length - 1;// 只要left <= right 就一直查找while (left <= right){int mid = (left + right) / 2; // mid放在循环内声明是因为mid也需要更新if(value > array[mid]){// 向右查找,更新left = mid + 1left = mid + 1;}else if(value < array[mid]){// 向左查找,更新right = mid - 1right = mid - 1;}else {return mid;}}// 出了循环说明没有找到,就返回-1return -1;}

6.运行结果

设待查找值设置为99 、 0,在array[N] = [1,10,20,30,99,1020]中查找。运行结果如下(99在数组内,故返回索引4;0不在数组中,故返回-1)

7.代码优化(针对线性表中有重复关键字的情况)

这一部分,我们以递归方式实现的代码优化为例,思路如下:
取示例array[N] = {1,10,20,30,99, 99, 99, 1020},我们需要查找99

  1. 先找到mid,但是不要直接返回mid
  2. 以mid为起点向左侧扫描,将所有关键字为99的下标记录到ArrayList集合
  3. 再次以mid为起点向右侧扫描,将所有关键字为99的下标记录到ArrayList集合
  4. 注意mid只用记录一次
  5. 返回ArrayList,遍历ArrayList即可得到所有索引

优化代码如下:

import java.util.ArrayList;public class BinarySearch {public static void main(String[] args) {int[] array = {1,10,20,30,99, 99, 99, 1020};// 待查找值设置为99 、 0int value1 = 99;int value2 = 0;System.out.println(value1 + "'s index: " + bs(array,0,array.length-1,value1));System.out.println(value2 + "'s index: " + bs(array,0,array.length-1,value2));}// 二分查找递归实现/**** @param array 数组* @param left 左端点* @param right 右端点* @param value 待查找的值* @return 查找到value则返回索引值;否则返回空集合*/public static ArrayList<Integer> bs(int[] array, int left, int right, int value) {// 没有找到if(left > right){return new ArrayList();}int mid = (left + right) / 2;if(value > array[mid]){// 向右查找,更新left = mid + 1return bs(array, mid + 1, right, value);}else if(value < array[mid]){// 向左查找,更新right = mid - 1return bs(array,  left, mid - 1, value);}else {ArrayList<Integer> res = new ArrayList<>();// 扫描左侧int index = mid - 1;while (true){if(index < 0 || array[index] != value){break;}res.add(index);index--;}// mid记录一次res.add(mid);// 扫描右侧index = mid + 1;while (true){if(index > array.length - 1 || array[index] != value){break;}res.add(index);index++;}return res;}}
}

8.运行结果(优化后)

取示例array[N] = {1,10,20,30,99, 99, 99, 1020},我们查找99、0,应该返回4、5、6和空。

— end
本文到此就结束啦,如果对你有帮助,顺手点个赞叭!

【数据结构与算法】一篇文章彻底搞懂二分查找(思路图解+代码优化)两种实现方式,递归与非递归相关推荐

  1. 一篇文章彻底搞懂Android事件分发机制

    本文讲的是一篇文章彻底搞懂Android事件分发机制,在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解 ...

  2. mysql snowflake_一篇文章彻底搞懂snowflake算法及百度美团的最佳实践

    写在前面的话 一提到分布式ID自动生成方案,大家肯定都非常熟悉,并且立即能说出自家拿手的几种方案,确实,ID作为系统数据的重要标识,重要性不言而喻,而各种方案也是历经多代优化,请允许我用这个视角对分布 ...

  3. 一篇文章全搞懂!B2B

    作为一名刚入行不久的电商新人,时常被各种专业名词搞的晕头转向,公司开会或者行业交流时候没听懂也只能假装呵呵一笑(话说这种呵呵一笑是什么态度!摔).痛心疾首,小编决定总结近期所有我听过的没听过的互联网专 ...

  4. 目标检测扩(六)一篇文章彻底搞懂目标检测算法中的评估指标计算方法(IoU(交并比)、Precision(精确度)、Recall(召回率)、AP(平均正确率)、mAP(平均类别AP) )

    ​ 基本在目标检测算法中会碰到一些评估指标.常见的指标参数有:IoU(交并比).Precision(精确度).Recall(召回率).AP(平均正确率).mAP(平均类别AP)等.这些评估指标是在评估 ...

  5. 一篇文章快速搞懂排序算法(含实现源码)

    十大排序算法函数声明 时间复杂度表 主函数 #include<iostream> #include<cstdlib> #include<srand> using n ...

  6. 一篇文章快速搞懂十大排序算法(C++实现源码)

    十大排序算法函数声明 时间复杂度表 主函数 #include<iostream> using namespace std;void BubbleSort(int length, int a ...

  7. 一篇文章 轻松搞懂 AC自动机

    索引 概念 前后缀匹配 Trie树 AC自动机的实现 初始化 Fail指针的构建 匹配字符串 一名蒟蒻向您问好. 概念 这是 AC自动机,不是自动AC机, 是一个十分常用的多模式字符串匹配算法 (也就 ...

  8. 八、一篇文章快速搞懂MySQL 常见的数据类型(整型、小数、字符型、日期型详解)

    常见的数据类型 1.数值型: 整型 小数: 定点数 浮点数 2.字符型: 较短的文本:char.varchar 较长的文本:text.blob(较长的二进制数据) 3.日期型: 一.整型 1)分类: ...

  9. 一篇文章彻底搞懂“分布式事务”

    在如今的分布式盛行的时代,分布式事务永远都是绕不开的一个话题,今天就谈谈分布式事务相关的一致性与实战解决方案. 01 为什么需要分布式事务 由于近十年互联网的发展非常迅速,很多网站的访问越来越大,集中 ...

最新文章

  1. 《iPhone与iPad开发实战—iOS经典应用剖析》连载二
  2. 网站建设你够专业吗?——不需说,从色彩搭配就能看出来
  3. SVN Git 设置忽略目录 大全
  4. spring cloud云服务架构 - particle云架构代码结构讲解
  5. 2014河北廊坊计算机一级,2018年上半年河北省廊坊市计算机等级考试简章
  6. PHP中获取html页面传值
  7. iphone11京东商品评论分析
  8. python标准库-math数学函数库介绍
  9. DTOJ 2746. 皇后游戏(game)
  10. 地理信息三维可视化技术在城市规划中的应用
  11. python调用百度地图实现导航_利用python和百度地图API实现数据地图标注
  12. 计算机重启后e盘没了,Win10电脑怎么隐藏磁盘分区?
  13. 双系统安装红旗linux,红旗LINUX怎么安装成双系统?
  14. telnet 命令退出命令
  15. 007:Scrapy核心架构和高级运用
  16. 华硕笔记本k555拆机图解_「华硕k401n」华硕K401笔记本电脑拆机清灰步骤详解 - seo实验室...
  17. 物联网应用入门--利用虚拟硬件模拟土壤湿度传感器应用编写
  18. Mysql主从复制手动切换步骤
  19. MDD(模型驱动开发)
  20. MICCAI 2022中的医学扩散模型

热门文章

  1. Assignment 2: UDP Pinger[课后作业]
  2. c语言将结果原模原样输出到文件,用c语言处理文件
  3. 英语总结系列:每天怀揣一点激情
  4. 法律部门和法律体系(概念、我国现行的法律部门和法律体系 )、法 律 关 系(概念、构成要素:主体、内容、客体)、法律事实(法律事件、法律行为)
  5. tomcat(一个牛人写的文章,自己看)
  6. debian更新apt源报错 ...is not valid yet (invalid for another 722d 21h 13min 35s).
  7. PostgreSQL 儒略历学习资料
  8. 带蒙版的安卓剪辑软件_想用手机做自媒体?推荐这几款剪辑软件
  9. 同一个局域网之内,如何远程控制对方的电脑而且不用对方同意
  10. 【免费开放源码】审批类小程序项目实战(活动申请页面)