Leetcode算法入门与数组丨5. 数组二分查找

文章目录

    • 1 二分查找算法
    • 2 二分查找细节
    • 3 二分查找两种思路
      • 3.1 直接法
      • 3.2 排除法
    • task09
    • task10

1 二分查找算法

二分查找算法是一种常用的查找算法,也被称为折半查找算法。它适用于有序数组的查找,并通过将待查找区间不断缩小一半的方式来快速定位目标值。

算法思想如下:

  1. 首先,确定待查找数组的起始位置(通常为数组的第一个元素)和结束位置(通常为数组的最后一个元素)。
  2. 然后,计算待查找区间的中间位置,即将起始位置和结束位置相加除以2。
  3. 比较中间位置的元素与目标值的大小关系:
  • 如果中间位置的元素等于目标值,则查找成功,返回中间位置。
  • 如果中间位置的元素大于目标值,则目标值可能在左半部分,将结束位置更新为中间位置的前一个位置。
  • 如果中间位置的元素小于目标值,则目标值可能在右半部分,将起始位置更新为中间位置的后一个位置。
  1. 重复步骤2和步骤3,直到找到目标值或者待查找区间为空(起始位置大于结束位置)为止。

二分查找算法的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn) ,其中 n n n 为数组的大小。由于每次查找都将待查找区间缩小一半,因此它比线性查找算法更加高效。

2 二分查找细节

区间开闭问题

  • 左闭右闭区间:注意初始化时, r i g h t = l e n ( n u m s ) − 1 right = len(nums)-1 right=len(nums)1,数组最后一个元素位置。
  • 左闭右开区间:注意初始化时, r i g h t = l e n ( n u m s ) right = len(nums) right=len(nums),数组最后一个元素的下一个位置。
  • 一般情况,全部使用「左闭右闭区间」这种写法

m i d mid mid 取值问题

常见的两种取值公式

mid = (left + right) // 2   # 使用较多
mid = (left + right + 1) // 2
  • 当待查找区间中的元素个数为奇数个,使用这两种取值公式都能取到中间元素的下标位置。
  • 当待查找区间中的元素个数为偶数个
    • mid = (left + right) // 2 能取到中间靠左边元素的下标位置。
    • mid = (left + right + 1) // 2 能取到中间靠右边元素的下标位置。

出界条件的判断

两种判断方式

left <= right
left < right
  • left <= right,并且查找的元素不在有序数组中,则 while 语句的出界条件是 left > right,也就是 left == right + 1,写成区间形式就是 [ r i g h t + 1 right+1 right+1, r i g h t right right],此时待查找区间为空,待查找区间中没有元素存在,此时终止循环时,可以直接返回 −1。
  • left < right,并且查找的元素不在有序数组中,则 while 语句出界条件是 left == right,写成区间形式就是 [ r i g h t right right, r i g h t right right]。此时区间不为空,待查找区间还有一个元素存在,我们并不能确定查找的元素不在这个区间中,此时终止循环时,如果直接返回 −1 就是错误的。

使用 left < right 的话,可以在出界之后增加一层判断,判断是否等于目标元素。

# ...while left < right:# ...return left if nums[left] == target else -1

此时,在跳出循环的时候,一定是 left == right,无需判断此时应该返回 left or right

搜索区间范围的选择

三种写法

left = mid + 1,right = mid - 1
left = mid + 1 ,right = mid
left = mid,right = mid - 1

具体哪一种写法和二分查找的两种思路有关。

  • 思路 1:「直接法」—— 在循环体中找到元素后直接返回结果。
  • 思路 2:「排除法」—— 在循环体中排除目标元素一定不存在区间。

3 二分查找两种思路

3.1 直接法

  1. 设定左右边界为数组两端,即 l e f t = 0 , r i g h t = l e n ( n u m s ) − 1 left=0,right=len(nums)−1 left=0right=len(nums)1,代表待查找区间为 [ l e f t , r i g h t ] [left,right] [left,right](左闭右闭区间)。
  2. 取两个节点中心位置 m i d mid mid ,先比较中心位置值 n u m s [ m i d ] nums[mid] nums[mid] 与目标值 t a r g e t target target 的大小。
    1. 如果 t a r g e t = = n u m s [ m i d ] target==nums[mid] target==nums[mid],则返回中心位置。
    2. 如果 t a r g e t > n u m s [ m i d ] target>nums[mid] target>nums[mid] ,则将左节点设置为 m i d + 1 mid+1 mid+1,然后继续在右区间 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right] 搜索。
    3. 如果 t a r g e t < n u m s [ m i d ] target<nums[mid] target<nums[mid],则将右节点设置为 m i d − 1 mid−1 mid1,然后继续在左区间 [ l e f t , m i d − 1 ] [left,mid−1] [left,mid1] 搜索。
  3. 如果左边界大于右边界,查找范围缩小为空,说明目标元素不存在,此时返回 −1。
class Solution:def search(self, nums: List[int], target: int) -> int:left, right = 0, len(nums) - 1# 在区间 [left, right] 内查找 targetwhile left <= right:# 取区间中间节点mid = left + (right - left) // 2# 如果找到目标值,则直接范围中心位置if nums[mid] == target:return mid# 如果 nums[mid] 小于目标值,则在 [mid + 1, right] 中继续搜索elif nums[mid] < target:left = mid + 1# 如果 nums[mid] 大于目标值,则在 [left, mid - 1] 中继续搜索else:right = mid - 1# 未搜索到元素,返回 -1return -1

3.2 排除法

  1. 设定左右边界为数组两端,即 l e f t = 0 , r i g h t = l e n ( n u m s ) − 1 left=0,right=len(nums)−1 left=0right=len(nums)1,代表待查找区间为 [ l e f t , r i g h t ] [left,right] [left,right](左闭右闭区间)。
  2. 取两个节点中心位置 m i d mid mid ,比较中心位置值 n u m s [ m i d ] nums[mid] nums[mid] 与目标值 t a r g e t target target 的大小,先将目标元素一定不存在的区间排除。
  3. 然后在剩余区间继续查找元素,继续根据条件排除目标元素一定不存在的区间。
  4. 直到区间中只剩下最后一个元素,然后再判断这个元素是否是目标元素。
class Solution:def search(self, nums: List[int], target: int) -> int:left, right = 0, len(nums) - 1# 在区间 [left, right] 内查找 targetwhile left < right:# 取区间中间节点mid = left + (right - left) // 2# nums[mid] 小于目标值,排除掉不可能区间 [left, mid],在 [mid + 1, right] 中继续搜索if nums[mid] < target:left = mid + 1 # nums[mid] 大于等于目标值,目标元素可能在 [left, mid] 中,在 [left, mid] 中继续搜索else:right = mid# 判断区间剩余元素是否为目标元素,不是则返回 -1return left if nums[left] == target else -1
  • 直接法:更适合解决简单题目,数组中是非重复元素。
  • 排除法:更适合解决复杂题目,数组里可能不存在的元素,找边界问题。

task09

704. 二分查找

思路

  • 上来就是一个二分查找
  • 取中间值,然后根据中间值和目标值之间的大小确定比较的区间
  • 如果不存在目标值就返回 -1

代码

class Solution:def search(self, nums: List[int], target: int) -> int:left, right = 0, len(nums) - 1      # 设定左右边界while left <= right:mid = (left + right) // 2       # 取中间节点if nums[mid] == target:         # 中间节点值等于目标值,返回中间位置return midelif nums[mid] > target:        # 中间节点值大于目标值,区间换成[left, mid-1]right = mid - 1 else:left = mid + 1              # 中间节点值小于目标值,区间换成[mid+1, right]return -1

在这里插入图片描述

35. 搜索插入位置

思路

  • 上手就是一个二分查找
  • 对于目标值不存在的情况,直接返回左端下标值,即被顺序插入的位置

代码

class Solution:def searchInsert(self, nums: List[int], target: int) -> int:left, right = 0, len(nums) - 1while left <= right:mid = (left + right) // 2if nums[mid] == target:return midelif nums[mid] > target:right = mid - 1else:left = mid + 1return left                 # 目标值不存在,直接返回左端下标值

在这里插入图片描述

374. 猜数字大小

思路

  • 本质上还是二分查找,只不过是使用预定义的接口比较大小

代码

# The guess API is already defined for you.
# @param num, your guess
# @return -1 if num is higher than the picked number
#          1 if num is lower than the picked number
#          otherwise return 0
# def guess(num: int) -> int:class Solution:def guessNumber(self, n: int) -> int:left, right = 1, nwhile left <= right:mid = (left + right) // 2result = guess(mid)if result == 0:return midelif result == -1:right = mid - 1else:left = mid + 1

在这里插入图片描述

task10

69. x 的平方根

思路

  • 本质上是二分查找,使用中间值的平方和目标值进行比较
  • 小于等于目标值,缩小左端,继续进行二分,直到中间值的平方大于目标值,返回中间值作为结果
  • 需要注意的是目标值是0和1的特殊情况

代码

class Solution:def mySqrt(self, x: int) -> int:left, right, res = 0, x, -1if x == 0 or x == 1:return xwhile left <= right:mid = (left + right) // 2if mid * mid <= x:res = midleft = mid + 1else:right = mid - 1return res

在这里插入图片描述

参考文献

  • [1] https://datawhalechina.github.io/leetcode-notes/#/

—— END ——


如果以上内容有任何错误或者不准确的地方,欢迎在下面 👇 留言。或者你有更好的想法,欢迎一起交流学习~~~

更多精彩内容请前往 AXYZdong的博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/143257.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 线程模式

Redis 是单线程吗&#xff1f; Redis 单线程指的是 [接收客户端请求 -> 解析请求 -> 进行数据读写操作 -> 发送数据给客户端] 这个过程是由一个线程 (主线程) 来完成的&#xff0c;这也是常说的 Redis 是单线程的原因。 但是 &#xff0c;Redis 程序不是单线程的&am…

VB从资源文件中播放wav音乐文件

Private Const SND_SYNC &H0 Private Const SND_MEMORY &H4 API函数 Private Declare Function sndPlaySoundFromMemory Lib "winmm.dll" Alias "sndPlaySoundA" (lpszSoundName As Any, ByVal uFlags As Long) As Long 音乐效果请“单击” Pr…

美国零售电商平台Target,值得入驻吗?如何入驻?

Target 是美国最大的零售商之一&#xff0c;在品牌出海为大势所趋的背景下&#xff0c;它在北美电商中的地位节节攀升。Target 商店在众多垂直领域提供各种价格实惠的自有品牌&#xff0c;吸引越来越多的跨境商家入驻&#xff0c;如美妆、家居、鞋服、日用百货等&#xff0c;随…

在比特币上支持椭圆曲线 BLS12–381

通过使用智能合约实现来支持任何曲线 BLS12–381 是一种较新的配对友好型椭圆曲线。 与常用的 BN-256 曲线相比&#xff0c;BLS12-381 的安全性明显更高&#xff0c;并且安全目标是 128 位。 所有其他区块链&#xff0c;例如 Zcash 和以太坊&#xff0c;都必须通过硬分叉才能升…

Android Studio 创建项目不自动生成BuildConfig文件

今天在AS上新建项目发现找不到BuildConfig文件&#xff0c;怎么clear都不行。通过多方面查找发现原来gradle版本不同造成的&#xff0c;Gradle 8.0默认不生成 BuildConfig 文件。 如上图&#xff0c;8.0版本是没有source文件夹 上图是低于8.0版本有source文件夹 针对这个问题&…

Anchors

这是源代码定义的anchors概念&#xff1a; 实现过程&#xff1a; 假如有一张500500的图片&#xff0c;那么经过第一步深度卷积网络之后&#xff08;4次池化&#xff09;&#xff0c;最终就会变成一个3232的特征&#xff1a; 在开源代码实现里面&#xff1a; 所以经过卷积完之后…

D. A Simple Task

Problem - D - Codeforces 思路&#xff1a;这个题就是求环的数量&#xff0c;通过数据范围的大小&#xff0c;我们可以想到用状压dp来做&#xff0c;因为只有19个点&#xff0c;我们可以将环的路径进行状态压缩&#xff0c;用一个二进制数表示环&#xff0c;当某一位为1时表示…

3、组件和容器

3、组件和容器 Frame 万物皆对象&#xff0c;窗口也是一个对象&#xff0c;这里Frame也是一个对象&#xff0c;我们可以看到Frame是可以new出来的&#xff0c;它是属于java.awt包下的 学习中想要知道这个类怎么用可以采用查JDK帮助文档&#xff0c;这里推荐查看源码&#xff0…

解决 MyBatis-Plus 中增加修改时,对应时间的更新问题

问题&#xff1a;在添加修改时&#xff0c;对应的 create_time 与 insert_time 不会随着添加修改而自动的更新时间 第一步&#xff1a;首先在对应的属性上&#xff0c;加上以下注解 如果只添加以下注解&#xff0c;在增加或者修改时&#xff0c;可能对应的 LocalDateTime 会出…

Unity中Shader需要了解的点与向量

文章目录 前言一、点和向量的区别二、向量加法减法1、向量加法2、向量减法(可以把向量减法转化为向量加法) 三、向量的模四、标量![在这里插入图片描述](https://img-blog.csdnimg.cn/03df81df3cdf47989a11605d5f5e7da5.png)1、向量与标量的乘法 前言 Unity中Shader了解使用的…

使用Python做一个微信机器人

介绍 简介 该程序将微信的内部功能提取出来&#xff0c;然后在程序里加载Python&#xff0c;接着将这些功能导出成库函数&#xff0c;就可以在Python里使用这些函数 程序启动的时候会执行py_code目录下的main.py&#xff0c;类似于你在命令行使用python main.py。 现在会以…

windows11系统没有系统散热方式的解决办法

一、问题描述 当我们查看Win11系统的&#xff08;同时按下键盘的WinR键即可打开运行窗口&#xff09;【控制面板】-->【硬件和声音】-->【电源选项】-->【更改计划设置】-->【 更改高级电源设置】-->【处理器电源管理】下没有系统散热方式的选项&#xff0c;如下…

【C语言】【结构体的内存对齐】计算结构体内存大小,有图解

计算结构体内存大小&#xff0c;需要用到结构体内存对齐的知识 来段代码看看什么是结构体对齐&#xff1a; #include<stdio.h> struct S1 {char a;char b;int num; }; struct S2 {char a;int num;char b; }; int main() {printf("%zd\n", sizeof(struct S1))…

Armv9 Cortex-A720的L2 memory system 和 L2 Cache

9 L2 memory system Cortex-A720核心的L2内存系统通过CPU bridge连接core与DynamIQ Shared Unit-120,其中包括私有的L2缓存。 L2缓存是统一的,每个Cortex-A720核心在一个集群中都有私有的L2缓存。 L2内存系统包括使用虚拟地址(VA)和程序计数器(PC)的数据预取引擎。不同…

SpringCloud nacos1.x.x版本升级到2.2.3版本并开启鉴权踩坑

近期由于服务器漏洞扫描&#xff0c;检测出nacos存在绕过登录鉴权漏洞&#xff0c;如图 需要进行升级并开启鉴权&#xff0c;就此次升级做下记录。 1.首先备份原来的nacos&#xff0c;导出配置文件作为备份&#xff1b; 2&#xff0c;从官网下载nacos-server-2.2.3.zip&#x…

rk3568环境配置和推理报错: RKNN_ERR_MALLOC_FAIL

前言 最近在部署算法在板子侧遇到的一些问题汇总一下&#xff1a; 一、版本问题 经过测试现在将自己环境配置如下&#xff1a; 本地linux安装rknn-toolkit2-1.5.0 本地Linux使用的miniconda新建的一个python虚拟环境&#xff08;自行网上查找相关方法&#xff09; 安装好自…

Android导航抽屉

本文所有代码均位于https://github.com/MADMAX110/CatChat 之前使用过标签页布局可以让用户在应用中轻松地导航。 当只有为数不多地几个类别屏幕&#xff0c;而且它们都在应用层次结构地同一级上&#xff0c;标签页布局就很适用。 而抽屉导航可以实现更多选择&#xff0c;这是一…

python(自4) xpath下载 lxml安装 lxml语法 使用方式

&#xff08;一&#xff09;安装 搜索xpath 讲解 XPath 教程 (w3school.com.cn) 一&#xff0c;下载地址 &#xff1a; https://chrome.zzzmh.cn/info/hgimnogjllphhhkhlmebbmlgjoejdpjl 二 &#xff0c;拖拽 &#xff08;二&#xff09;lxml安装 cmd 打开终端 cd pythond…

JAVA中使用CompletableFuture进行异步编程

JAVA中使用CompletableFuture进行异步编程 1、什么是CompletableFuture CompletableFuture 是 JDK8 提供的 Future 增强类&#xff0c;CompletableFuture 异步任务执行线程池&#xff0c;默认是把异步任 务都放在 ForkJoinPool 中执行。 在这种方式中&#xff0c;主线程不会…

Textpad 缺少Java编译和运行功能

一、问题 缺少Java编译和运行功能 二、处理方法 1、点击菜单Configure->Preferences 2、点击 Tools -> Add -> Java SDK Commands 3、点击应用和确认 三、结果