算法初学者学二分查找

news2024/11/13 9:05:11

力扣704,二分查找

题目是这样的

二分查找的思路就是:

先确定左右两个边界,左边界是从左往右,右边界是从右往左,所以,左边界是找的比target大的第一个值,右边界最后停的点是比target小的第一个值。

他分为左闭右闭区间和左闭右开两种情况 

  • 左闭右闭([left, right]),此时的right是可以取到的,那么while循环的条件就是“left <= right”
  • 左闭右开([left, right)),此时的right是取不到的,那么while循环的条件是"left  < right"
  • 由于右边开区间,那么最右边的值取不到,在while的循环体,right迭代的时候,right = middle,在定义right的时候,right = len(nums) 

接着,比left小,比right大的中间的middle值就是我们要的target的位置,middle = left + (right-left)//2,为什么不是 middle = (left + right)// 2呢,此处是防止整数溢出,left+right容易超出int的范围,所以写成middle = left + (right-left)//2,"//2"是处以2,名且下取整的意思。

此时开启我们的循环,下面十个例子,nums = [1,2,3,4,7,9,10],target = 2,left = 0, right = len(nums) -1,注意,left和right是索引!当nums[middle] > target,那就往右边走,小于就往左边走。

代码如下:

# [left, right]
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1  # 定义target在左闭右闭的区间里,[left, right]

        while left <= right:
            middle = left + (right - left) // 2
            
            if nums[middle] > target:
                right = middle - 1  # target在左区间,所以[left, middle - 1]
            elif nums[middle] < target:
                left = middle + 1  # target在右区间,所以[middle + 1, right]
            else:
                return middle  # 数组中找到目标值,直接返回下标
        return -1  # 未找到目标值
# [left, right)
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums)  # 定义target在左闭右开的区间里,即:[left, right)

        while left < right:  # 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            middle = left + (right - left) // 2

            if nums[middle] > target:
                right = middle  # target 在左区间,在[left, middle)中
            elif nums[middle] < target:
                left = middle + 1  # target 在右区间,在[middle + 1, right)中
            else:
                return middle  # 数组中找到目标值,直接返回下标
        return -1  # 未找到目标值

上面是二分查找最经典的解题思路,下面是几个二分查找的变式

力扣35: 搜索插入位置 (二分查找变式)

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:

  • 输入: [1,3,5,6], 5
  • 输出: 2

示例 2:

  • 输入: [1,3,5,6], 2
  • 输出: 1

示例 3:

  • 输入: [1,3,5,6], 7
  • 输出: 4

示例 4:

  • 输入: [1,3,5,6], 0
  • 输出: 0

思路就是:采用二分查找的方法,最后返回right + 1而不是-1,因为right是比target小的第一个数,在他后面插入的位置在,所以是+1

[left, right]
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1

        while left <= right:
            middle = (left + right) // 2

            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return right + 1
# [left, right)
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)

        while (left < right):
            middle = (left + right) // 2

            if nums[middle] > target:
                right = middle
            elif nums[middle] < target:
                left = middle + 1
            else:
                return middle

        return right

力扣34:在排序数组中查找元素的第一个和最后一个位置(二分查找变式)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:你可以设计并实现时间复杂度为 $O(\log n)$ 的算法解决此问题吗?

示例 1:

  • 输入:nums = [5,7,7,8,8,10], target = 8
  • 输出:[3,4]

示例 2:

  • 输入:nums = [5,7,7,8,8,10], target = 6
  • 输出:[-1,-1]

示例 3:

  • 输入:nums = [], target = 0
  • 输出:[-1,-1]

思路:

有三种情况

target在nums范围中,其中又有两种情况,一种是target在nums里,这种情况的话就要确定左右边界,另一种就是不在,直接返回[-1, -1]

target不在nums范围中,直接返回[-1, -1]

他这个就比之前那个难一些,要确定target位置,且数组中的值不是唯一的,就要从左右两端分别通过确定左边界,和右边界来入手了,要分别定义两个函数,一个是确定左边界的,一个是定义右边界的,我说一下定义左边边界,右边界同理,首先,和之前一样,先确定left和right的取值范围,这是写在左边界函数里的,left = 0,right = len(nums) -1(讨论左右都闭的情况),先取leftboundary=-2,你可以让他等于任何值,while的条件还是不变,当nums[middle] > target时,right = middle - 1,其他情况,left = middle +1,同时更新leftboundary = left,因为此时左边界发生了变化,同理得到rightboundary,如果他们俩的值都是-2,说明nums中没有target,返回[-1, -1],如果rightboundary  > rightboundary,就返回[leftboundary + 1,rightboundary - 1],其他就是[-1,-1]

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def getRightBorder(nums:List[int], target:int) -> int:
            left, right = 0, len(nums)-1
            rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
            while left <= right:
                middle = left + (right-left) // 2
                if nums[middle] > target:
                    right = middle - 1
                else: # 寻找右边界,nums[middle] == target的时候更新left
                    left = middle + 1
                    rightBoder = left
    
            return rightBoder
        
        def getLeftBorder(nums:List[int], target:int) -> int:
            left, right = 0, len(nums)-1 
            leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
            while left <= right:
                middle = left + (right-left) // 2
                if nums[middle] >= target: #  寻找左边界,nums[middle] == target的时候更新right
                    right = middle - 1
                    leftBoder = right
                else:
                    left = middle + 1
            return leftBoder
        leftBoder = getLeftBorder(nums, target)
        rightBoder = getRightBorder(nums, target)
        # 情况一
        if leftBoder == -2 or rightBoder == -2: return [-1, -1]
        # 情况三
        if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
        # 情况二
        return [-1, -1]

还有一种滑动指针的方法,复杂度比较低,思路就是,前面用二分查找,引入一个索引,如果这个索引等于-1,说明nums中没有target,返回[-1, -1].,其他情况的话,确定两个指针left,right,让他们的初始值都为middle,往左滑,左边界不低于0,所以while(left - 1 < 0),如果nums[left-1]=target,就更新left的值,left -= 1,此时的left就是左边界,往右滑,找右边界,右边界不低于nums的长度,更新right,然后返回[left, right]

# 解法2
# 1、首先,在 nums 数组中二分查找 target;
# 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
# 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def binarySearch(nums:List[int], target:int) -> int:
            left, right = 0, len(nums)-1
            while left<=right: # 不变量:左闭右闭区间
                middle = left + (right-left) // 2
                if nums[middle] > target:
                    right = middle - 1
                elif nums[middle] < target: 
                    left = middle + 1
                else:
                    return middle
            return -1
        index = binarySearch(nums, target)
        if index == -1:return [-1, -1] # nums 中不存在 target,直接返回 {-1, -1}
        # nums 中存在 target,则左右滑动指针,来找到符合题意的区间
        left, right = index, index
        # 向左滑动,找左边界
        while left -1 >=0 and nums[left - 1] == target: left -=1
        # 向右滑动,找右边界
        while right+1 < len(nums) and nums[right + 1] == target: right +=1
        return [left, right]

力扣 69 x的平方根(二分查找的变式)

思路就是,把x当成target,把[0, x]的整数当成nums,取中间的middle,看middle* middle和target的关系

class Solution:
    def mySqrt(self, x: int) -> int:
        left , right = 0, x
        while(left <= right):
            middle = left + (right - left)//2
            if middle * middle > x:
                right = middle -1
            elif middle * middle < x:
                left = middle + 1
            else:
                return middle
        return right

 力扣367.有效的完全平方数(二分查找变式)

思路同上

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left, right = 0, num
        while(left <= right):
            middle = left + (right - left)//2
            if middle * middle < num:
                left = middle + 1
            elif middle * middle > num:
                right = middle - 1
            else:
                return True
        return False

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谢希仁计算机网络第八版期末复习简答(1)

绪论 计算机网络的概念以及常见的网络类型? 计算机网络&#xff0c;是指地理位置分散的、具有独立功能的多台计算机及其外部设备&#xff0c;通过各种通信线路互联起来&#xff0c;在网络操作系统、网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息…

2024年的AI人工智能风口是Python?一篇文章告诉你为什么!

Python是一种面向对象的、解释型的、通用的、开源的脚本编程语言&#xff0c;它之所以非常流行&#xff0c;我认为主要有三点原因&#xff1a; 1.Python 简单易用&#xff0c;学习成本低&#xff0c;看起来非常干净&#xff1b; 2.Python 标准库和第三库众多&#xff0c;功能…

短剧系统源码分享,快速搭建部署上线教程

一、短剧系统是什么&#xff1f; 短剧制作平台&#xff0c;作为一站式综合解决方案&#xff0c;集剧本创作、角色设计、场景搭建、视频编辑、便捷发布及深度数据分析能力于一身。该平台精准定位于助力企业利用短剧形式强化品牌传播力并驱动商业价值增长&#xff0c;无论企业是…

物理学中三个令人兴奋的想法:稳态作用原理、洛伦兹变换和度量张量

​ 图片来源&#xff1a; https 文章目录 ​一、说明二、最小行动原则三、洛伦兹变换四、Minkowski 公制五、度量张量&#xff1a;曲面的几何形状六、三维欧几里得距离。 ​一、说明 虽然物理学引起了公众的好奇心&#xff0c;但许多人发现数学令人生畏。然而&#xff0c;物理…

C#知识|文件与目录操作:目录的操作

哈喽,你好啊,我是雷工! 前边学习了文件的删除、复制、移动,接下来学习目录的操作。 以下为学习笔记。 01 效果演示 1.1、显示指定目录下的所有文件 在左侧的文本框中显示出F:\F004-C#目录下的所有文件, 演示效果: 1.2、显示指定目录下的所有子文件 在左侧的文本框中显…

Serverless Knative冷启动与自动扩缩容研究:从原理到实践

最近一个研究生网页的提问&#xff0c;然后就有了这篇博客&#xff01; 大佬你好&#xff0c;我看到您的关于Serverless的文章于是十分冒昧的向您提问。我现在是一名在研究通过Serverless容器调度解决冷启动问题的本科生&#xff0c;导师放养&#xff0c;就让看论文但是后面的代…

多态、接口、类练习题

代码&#xff1a; public static void main(String[] args) {Person2 personnew Person2("唐僧",new Horse());person.passRiver();person.onRoad();} 接口&#xff1a; interface Vehicles{public void work(); } lass Horse implements Vehicles{Overridepubli…

HIS系统开发,医院HIS系统开发注意事项

在当今信息化迅速发展的时代&#xff0c;医院信息系统&#xff08;HIS&#xff09;的开发已成为提升医疗服务效率和质量的关键。HIS系统的开发并非易事&#xff0c;它涉及到众多复杂的技术和管理问题。以下是开发HIS系统时需要特别注意的几个关键问题。 数据安全与隐私保护是HI…

正点原子imx6ull-mini-Linux驱动之Linux 并发与竞争实验(8)

在上一章中我们学习了 Linux 下的并发与竞争&#xff0c;并且学习了四种常用的处理并发和竞争的 机制&#xff1a;原子操作、自旋锁、信号量和互斥体。本章我们就通过四个实验来学习如何在驱动中使 用这四种机制。 1&#xff1a;原子操作实验 本例程我们在第四十五章的 gpiol…

不想交智商税的都收藏了!K9、希喂、渴望,最近风超大的猫咪主食罐测评

宠物行业有多卷咱就不说了&#xff0c;反正某宝一打开品牌多达上百个&#xff0c;各种猫粮产品更是数不胜数&#xff01;不懂点门道的人选起来确实有点费脑子。不过我养猫这七八年来&#xff0c;前前后后入手过的主食罐头少说也有三十来款&#xff0c;自认还是积累了一些经验。…

《python语言程序设计》2018版第6章第21题平方根的近似求法math模块里有几种实现sqrt函数的方法

只能针对“1111” 或者“77”这样的&#xff0c;其它的不行。但是很成功了 def sqrt(n):lastGuess 0#我让他们两个相等&#xff0c;来建立循环的条件nextGuess n#这样就给了lastGuess和nextGuess进行循环的条件while lastGuess ! nextGuess:lastGuess 1nextGuess (lastGue…

【宝藏系列】物联网中常用的十种通信协议

【宝藏系列】物联网中常用的十种通信协议 文章目录 【宝藏系列】物联网中常用的十种通信协议1️⃣MQTT2️⃣CoAP3️⃣AMQP4️⃣XMPP5️⃣LwM2M6️⃣HTTP7️⃣DDS8️⃣Bluetooth Low Energy9️⃣LoRaWAN1️⃣0️⃣NB-IoT 1️⃣MQTT MQTT&#xff08;Message Queuing Telemetry T…

51单片机和STM32区别

51单片机和 STM32 区别 51单片机和 STM32 是两种常见的微控制器&#xff0c;它们在架构、性能、外设接口、功耗和开发环境等方面有所不同。 1. 架构差异 51单片机基于传统的哈佛总线结构&#xff0c;采用 CISC 架构&#xff0c;而 STM32 基于 ARM Cortex-M 系列的32位处理器核…

JavaScript高级程序设计教程(第3版 电子版教程)

前言 JavaScript 是一种非常松散的面向对象语言&#xff0c;也是 Web 开发中极受欢迎的一门语言。JavaScript&#xff0c;尽管它的语法和编程风格与 Java 都很相似&#xff0c;但它却不是 Java 的“轻量级”版本&#xff0c;甚至与 Java 没有任何关系。JavaScript 是一种全新的…

vue一些npm i 时报错问题解决【JAVA前后端分离】

前端vue npm i 安装时出现 作为一个懂些前端得 JAVA开发自然是要粗暴解决这个问题了 问题解决 使用命令 npx -p npm6 npm i 即可编译 原因&#xff1a; ERESOLVE与npm版本有关&#xff0c;因为npm版本高对某些事情比npm6.x更严格。通常&#xff0c;最简单的解决方法是将--…

农合生活平台更新升级啦!了解详情戳这里

7月24日&#xff0c;农合生活平台完成了新一轮的版本更新。新版本上线后&#xff0c;农元NYT购买数量将不做限制&#xff0c;优惠券更易得&#xff0c;购物更划算&#xff0c;农元价值升值将进一步「加速度」。 更新说明 1. 数量&#xff1a;旧版本中农元只能定额定量购买&…

卡奥斯智能交互引擎:引领工业智能,共创数字化未来

​智能交互引擎&#xff1a;https://www.cosmoplat.cnhttps://datayi.cn/w/nPN4v4e9 &#x1f4d6;一、平台简介 卡奥斯智能交互引擎聚焦工业领域,是卡奥斯结合海尔40年的工业制造经验积累和7年工业互联网平台建设的最佳工业智能实践。该引擎集成了工业大模型技术,基于卡奥斯…

Windows Server搭建Prometheus+Grafana监控系统

1.相关资源下载 所需资源百度网盘下载链接&#xff1a;https://pan.baidu.com/s/1wGH4KL085roIYBkTqmM5Og?pwd1111&#xff0c;也可根据下述方法&#xff0c;在官网进行下载。 1.1 Prometheus下载 访问地址https://prometheus.io/download/&#xff0c;点击prometheus-2.53…

项目管理中的关键:进度管理

项目管理中&#xff0c;进度管理无疑占据了举足轻重的地位&#xff0c;它不仅关乎项目能否按时完成&#xff0c;更是衡量项目效率、成本控制及质量保障的重要标尺。 它要求管理者根据项目的总体目标&#xff0c;科学合理地制定项目进度计划&#xff0c;并在项目实施过程中进行…