二分查找的概念

ops/2024/10/25 10:29:54/

文章目录

二分查找的原理

如果需要在 n n n 个数中查找目标值是否存在,最常规的思想是遍历所有的数,判断每个数是否等于目标值。遍历的时间复杂度是 O ( n ) O(n) O(n)

如果 n n n 个数是有序的,则可以使用更低的时间复杂度查找目标值是否存在。假设 n n n 个数按照升序排序,对于这 n n n 个数中的一个数字 x x x,将其与目标值比较,可能有以下三种情况:

  • 如果 x x x 等于目标值,则找到目标值,因此目标值存在;

  • 如果 x x x 大于目标值,则所有大于等于 x x x 的数都不可能是目标值,因此应该在小于 x x x 的数中查找目标值;

  • 如果 x x x 小于目标值,则所有小于等于 x x x 的数都不可能是目标值,因此应该在大于 x x x 的数中查找目标值。

根据上述思想,如果每次查找时选取的 x x x 是目标值可能存在的范围内的中间值,则经过一次查找可以将目标值可能存在的范围缩小一半。查找的次数是 O ( log ⁡ n ) O(\log n) O(logn),每次查找的时间是 O ( 1 ) O(1) O(1),因此时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn),显著低于遍历的时间复杂度 O ( n ) O(n) O(n)

上述在有序数字中查找目标值的方法称为二分查找,也称折半查找。

以下用一个例子说明二分查找的过程。

给定长度为 10 10 10 的数组: [ 0 , 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 ] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] [0,2,4,6,8,10,12,14,16,18],目标值是 4 4 4

low \textit{low} low high \textit{high} high 分别表示目标值可能存在的范围的下界和上界,初始时目标值可能存在的范围是整个数组, low = 0 \textit{low} = 0 low=0 high = 9 \textit{high} = 9 high=9。每次查找时,取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,判断下标 mid \textit{mid} mid 处的数和目标值的大小关系,调整查找范围。

  1. 根据 low = 0 \textit{low} = 0 low=0 high = 9 \textit{high} = 9 high=9 计算得到 mid = 4 \textit{mid} = 4 mid=4,下标 4 4 4 处的元素是 8 8 8,大于目标值 4 4 4,因此目标值一定在下标 mid \textit{mid} mid 的左侧,将 high \textit{high} high 更新为 mid − 1 \textit{mid} - 1 mid1,更新后 high = 3 \textit{high} = 3 high=3

  2. 根据 low = 0 \textit{low} = 0 low=0 high = 3 \textit{high} = 3 high=3 计算得到 mid = 1 \textit{mid} = 1 mid=1,下标 1 1 1 处的元素是 2 2 2,小于目标值 4 4 4,因此目标值一定在下标 mid \textit{mid} mid 的右侧,将 low \textit{low} low 更新为 mid + 1 \textit{mid} + 1 mid+1,更新后 low = 2 \textit{low} = 2 low=2

  3. 根据 low = 2 \textit{low} = 2 low=2 high = 3 \textit{high} = 3 high=3 计算得到 mid = 2 \textit{mid} = 2 mid=2,下标 2 2 2 处的元素是 4 4 4,等于目标值 4 4 4,因此找到目标值,位于下标 2 2 2

如果目标值是 5 5 5,则查找过程如下。

  1. 根据 low = 0 \textit{low} = 0 low=0 high = 9 \textit{high} = 9 high=9 计算得到 mid = 4 \textit{mid} = 4 mid=4,下标 4 4 4 处的元素是 8 8 8,大于目标值 5 5 5,因此目标值一定在下标 mid \textit{mid} mid 的左侧,将 high \textit{high} high 更新为 mid − 1 \textit{mid} - 1 mid1,更新后 high = 3 \textit{high} = 3 high=3

  2. 根据 low = 0 \textit{low} = 0 low=0 high = 3 \textit{high} = 3 high=3 计算得到 mid = 1 \textit{mid} = 1 mid=1,下标 1 1 1 处的元素是 2 2 2,小于目标值 5 5 5,因此目标值一定在下标 mid \textit{mid} mid 的右侧,将 low \textit{low} low 更新为 mid + 1 \textit{mid} + 1 mid+1,更新后 low = 2 \textit{low} = 2 low=2

  3. 根据 low = 2 \textit{low} = 2 low=2 high = 3 \textit{high} = 3 high=3 计算得到 mid = 2 \textit{mid} = 2 mid=2,下标 2 2 2 处的元素是 4 4 4,小于目标值 5 5 5,因此目标值一定在下标 mid \textit{mid} mid 的右侧,将 low \textit{low} low 更新为 mid + 1 \textit{mid} + 1 mid+1,更新后 low = 3 \textit{low} = 3 low=3

  4. 根据 low = 3 \textit{low} = 3 low=3 high = 3 \textit{high} = 3 high=3 计算得到 mid = 3 \textit{mid} = 3 mid=3,下标 3 3 3 处的元素是 6 6 6,大于目标值 5 5 5,因此目标值一定在下标 mid \textit{mid} mid 的左侧,将 high \textit{high} high 更新为 mid − 1 \textit{mid} - 1 mid1,更新后 high = 2 \textit{high} = 2 high=2。此时 low > high \textit{low} > \textit{high} low>high,因此目标值不存在。

二分查找的适用场景

由于二分查找的原理是根据当前查找的数与目标值的大小关系缩小查找的范围,因此可以使用二分查找的前提条件是单调性。如果不具备单调性,则无法通过二分查找得到正确的结果。

二分查找有两个常见的适用场景:一是查找目标值是否存在;二是在特定范围中查找符合要求的最大值或最小值,称为二分查找判定问题。

对于第一个场景,常见的是在有序数组中查找目标值,变体包括将有序数组旋转之后查找目标值或最大/最小值。

对于第二个场景,需要利用单调性找到最值。假设符合要求的最值是 x x x。当 x x x 是符合要求的最大值时,小于等于 x x x 的值都符合要求,大于 x x x 的值都不符合要求;当 x x x 是符合要求的最小值时,大于等于 x x x 的值都符合要求,小于 x x x 的值都不符合要求。

二分查找的代码实现

二分查找的模板有很多。此处提供三种实现,适用于不同的场景。

第一种实现为最简单的二分查找实现,在给定的数组中查找目标值所在下标,当目标值不存在时返回 − 1 -1 1。如果数组中有多个元素等于目标值,则返回其中一个元素所在下标。

class Solution {public int binarySearch(int[] nums, int target) {int low = 0, high = nums.length - 1;while (low <= high) {int mid = low + (high - low) / 2;if (nums[mid] == target) {return mid;} else if (nums[mid] > target) {high = mid - 1;} else {low = mid + 1;}}return -1;}
}

第二种实现为在给定的数组中查找大于等于目标值的最小元素所在下标,当数组中的所有元素都小于目标值时返回数组长度。

class Solution {public int binarySearch(int[] nums, int target) {int low = 0, high = nums.length;while (low < high) {int mid = low + (high - low) / 2;if (nums[mid] >= target) {high = mid;} else {low = mid + 1;}}return low;}
}

第三种实现为在给定的数组中查找小于等于目标值的最大元素所在下标,当数组中的所有元素都大于目标值时返回 − 1 -1 1

class Solution {public int binarySearch(int[] nums, int target) {int low = -1, high = nums.length - 1;while (low < high) {int mid = low + (high - low + 1) / 2;if (nums[mid] <= target) {low = mid;} else {high = mid - 1;}}return low;}
}

关于上述三种实现的说明如下。

  1. 上述三种实现为数组的场景,也可以推广到非数组的场景。后两种实现在二分查找判定问题中经常使用。

  2. 上述三种实现中,前两种实现的 mid \textit{mid} mid 的计算等价于 mid = ⌊ low + high 2 ⌋ \textit{mid} = \Big\lfloor \dfrac{\textit{low} + \textit{high}}{2} \Big\rfloor mid=2low+high,第三种实现的 mid \textit{mid} mid 的计算等价于 mid = ⌈ low + high 2 ⌉ \textit{mid} = \Big\lceil \dfrac{\textit{low} + \textit{high}}{2} \Big\rceil mid=2low+high,代码中的写法是为了避免溢出。

  3. 对于二分查找的实现,应注意每次查找之后都必须缩小查找范围,避免出现死循环。

目录

  1. 二分查找题目:二分查找
  2. 二分查找题目:搜索插入位置
  3. 二分查找题目:猜数字大小
  4. 二分查找题目:搜索二维矩阵
  5. 二分查找题目:x 的平方根
  6. 二分查找题目:第一个错误的版本
  7. 二分查找题目:在排序数组中查找元素的第一个和最后一个位置
  8. 二分查找题目:有序数组中的单一元素
  9. 二分查找题目:搜索旋转排序数组
  10. 二分查找题目:寻找旋转排序数组中的最小值
  11. 二分查找题目:H 指数 II
  12. 二分查找题目:搜索二维矩阵 II
  13. 二分查找题目:爱吃香蕉的珂珂
  14. 二分查找题目:在 D 天内送达包裹的能力
  15. 二分查找题目:使结果不超过阈值的最小除数
  16. 二分查找题目:制作 m 束花所需的最少天数
  17. 二分查找题目:两球之间的磁力
  18. 二分查找题目:搜索旋转排序数组 II
  19. 二分查找题目:寻找旋转排序数组中的最小值 II
  20. 二分查找题目:寻找右区间
  21. 二分查找题目:寻找峰值
  22. 二分查找题目:寻找峰值 II
  23. 二分查找题目:乘法表中第 k 小的数
  24. 二分查找题目:在线选举
  25. 二分查找题目:基于时间的键值存储
  26. 二分查找题目:快照数组
  27. 二分查找题目:寻找两个正序数组的中位数
  28. 二分查找题目:山脉数组中查找目标值

http://www.ppmy.cn/ops/128305.html

相关文章

Matlab|基于氢储能的热电联供型微电网优化调度方法

目录 1 主要内容 模型求解流程 2 部分程序 3 程序结果 日前调度 日内调度 4 下载链接 1 主要内容 该程序复现《基于氢储能的热电联供型微电网优化调度方法》&#xff0c;针对质子交换膜燃料电池和电解槽的热电联供特性&#xff0c;为避免氢能系统的热能浪费并进一步提高…

毕业设计:python美食菜谱数据分析可视化系统 爬虫+Echarts 可视化 Django框架 大数据(源码+文档2)✅

python美食菜谱数据分析可视化系统 爬虫Echarts 可视化 Django框架 大数据✅ 1、项目介绍 技术栈&#xff1a; Python语言、Django框架、vue框架、Echarts 可视化、MySQL 数据库、豆果美食网、html css js juery 基于django的美食菜谱数据分析可视化系统 2、项目界面 &…

从 IP 源地址入手,如何预防 DDoS 攻击?

分布式拒绝服务攻击&#xff08;DDoS&#xff09;是网络安全的一大威胁。DDoS 攻击通过大量的虚假请求&#xff0c;使目标服务器资源耗尽&#xff0c;无法正常为合法用户提供服务。而从 IP 源地址入手&#xff0c;是预防 DDoS 攻击的一个重要途径。 一、了解 DDoS 攻击与 IP 源…

K8s曝9.8分漏洞,黑客可获得Root访问权限

近日&#xff0c;安全研究人员Nicolai Rybnikar 发现Kubernetes镜像构建器中存在严重安全漏洞&#xff08;CVE-2024-9486 &#xff0c;CVSS &#xff1a;9.8&#xff09;&#xff0c;攻击者可在特定情况下获得Root级访问权限&#xff0c;从而导致系统出现问题。 Nicolai Rybnik…

接口测试(八)jmeter——参数化(CSV Data Set Config)

一、CSV Data Set Config 需求&#xff1a;批量注册5个用户&#xff0c;从CSV文件导入用户数据 1. 【线程组】–>【添加】–>【配置元件】–>【CSV Data Set Config】 2. 【CSV数据文件设置】设置如下 3. 设置线程数为5 4. 运行后查看响应结果

Go 语言基础教程:7.Switch 语句

在这篇教程中&#xff0c;我们将学习 Go 语言中的 switch 语句&#xff0c;它是条件分支的重要结构。我们将通过一个示例程序逐步解析 switch 的不同用法。 package mainimport ("fmt""time" )func main() {i : 2fmt.Print("Write ", i, " …

深入探讨 HTTP 请求方法:GET、POST、PUT、DELETE 的实用指南

文章目录 引言GET 方法POST 方法PUT 方法DELETE 方法小结适用场景与特点总结最佳实践 在 API 设计中的重要性 引言 HTTP 协议的背景&#xff1a;介绍 HTTP&#xff08;超文本传输协议&#xff09;作为互联网的基础协议&#xff0c;自 1991 年发布以来&#xff0c;成为客户端和…

并发基础知识

并发基础知识 进程和线程的区别 进程 每一个进程都拥有自己独立的内存空间等系统资源。进程与进程之间是相互独立的&#xff0c;都有自己的虚拟地址空间&#xff0c;一个进程出现问题崩溃&#xff0c;不会影响到其他的进程。进程与进程之间的通信比较复杂&#xff0c;一般是…