【灵神高频面试题合集04-05】二分查找

基础算法精讲·题目汇总:灵茶山艾府 - 【基础算法精讲】- GitHub

视频:灵茶山艾府的个人空间-灵茶山艾府个人主页-哔哩哔哩视频


二分查找

【题单】二分:https://leetcode.cn/circle/discuss/SqopEo/

04 二分查找 红蓝染色法

课程讲解

原始二分查找(模板代码)

要求 nums 是非递减的,即 numsi <= numsi+1,返回最小的满足 numsi >= target 的 i。如果不存在,返回 len(nums)

二分查找在闭区间上的写法,以及对比开区间、半闭半开区间上的写法

≥、>target(的第一个数)、≤、<target(的最后一个数)写法的差别

  • >x 等价于 ≥ x+1(的第一个数)
  • <x 可以看成 ≥ x 的第一个数,它左边的那个数
  • ≤x 可以看成 > x 的第一个数,它左边的那个数

红色更新 left 指针,蓝色更新 right 指针

python 复制代码
# 左闭右闭
def search(self, nums: List[int], target: int) -> int:
    left = 0
    right = len(nums) - 1
    # [left, right]
    while left <= right:   # 区间不为空
        mid = (left + right) // 2   # 或写成 left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1   # [mid+1, right]
        else:
            right = mid - 1   # [left, mid-1]
    return left

# 左闭右开
def search(self, nums: List[int], target: int) -> int:
    left = 0
    right = len(nums)
    # [left, right)
    while left < right:   # 区间不为空
        mid = (left + right) // 2   # 或写成 left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1   # [mid+1, right)
        else:
            right = mid   # [left, mid)
    return left   # 或 return right 均可

# 左开右开
def search(self, nums: List[int], target: int) -> int:
    left = -1
    right = len(nums)
    # (left, right)
    while left+1 < right:   # 区间不为空
        mid = (left + right) // 2   # 或写成 left + (right - left) // 2
        if nums[mid] < target:
            left = mid    # (mid, right)
        else:
            right = mid   # (left, mid)
    return right
  • 由于每次都去掉了一半的元素,时间复杂度为 O(logn)
  • 空间复杂度 O(1),没有用到额外空间
34. 在排序数组中查找元素的第一个和最后一个位置

等于求 target 的开始位置和结束位置,即分别是 ≥ 和 ≤

python 复制代码
class Solution:
    # 左闭右闭版本的模板代码
    def search(self, nums, target):
        left = 0
        right = len(nums) - 1
        # [left, right]
        while left <= right:   # 区间不为空
            mid = (left + right) // 2   # 或写成 left + (right - left) // 2
            if nums[mid] < target:
                left = mid + 1   # [mid+1, right]
            else:
                right = mid - 1   # [left, mid-1]
        return left
        
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        start = self.search(nums, target)   # ≥ target的第一个位置
        # 如果所有数都 < target 或 这个数不等于target
        if start == len(nums) or nums[start] != target:
            return [-1, -1]
        # ≤ target的最后一个位置,可以转化成
        # > target的第一个数,它左边的那个数
        # > target等价于 ≥ target + 1
        end = self.search(nums, target+1) - 1   # -1表示它左边的那个数
        return [start, end]
  • 时间O(logn),空间O(1)

课后作业

2529. 正整数和负整数的最大计数

2300. 咒语和药水的成功对数

1385. 两个数组间的距离值

2080. 区间内查询数字的频率

2563. 统计公平数对的数目

875. 爱吃香蕉的珂珂

2187. 完成旅途的最少时间

275. H 指数 II

2861. 最大合金数

2439. 最小化数组中的最大值

2517. 礼盒的最大甜蜜度


05 数组峰值 搜索旋转排序数组

课程讲解

162. 寻找峰值

找到一个峰顶,大于左右两侧相邻的元素

  • 比如下图中的2(第一个2)、4、6(因为可以假设 nums[-1] = nums[n] = -∞ )都是峰顶
  • 由于峰顶一定在数组中,所以数组最右侧的元素一定是蓝色的(n-1要么是峰顶,要么在峰顶右侧)
  • 因此二分时,可以初始化 left=0,right=n-2(n-1一定是蓝色,无需再二分)
  • 可以通过比较 M 和 M+1 指向的数字来染色。题目保证了这两个数字一定不相等(对于所有有效的 i 都有 nums[i] != nums[i + 1]),所以要么小于,要么大于
  • 若是小于,说明 M 在峰顶左侧(M右侧存在峰顶),都是红色,更新left
  • 若是大于,说明 M 要么是峰顶,要么在峰顶右侧(M左侧存在峰顶),都是蓝色,更新right
  • 二分循环结束后,L就是答案(左闭右闭写法时)
python 复制代码
# 左闭右闭写法
class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        # [0, n-2]
        left, right = 0, len(nums)-2
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] < nums[mid+1]:
                left = mid + 1
            else:
                right = mid - 1
        return left

# 左开右开写法
class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        # [0, n-2]
        # (-1, n-1)
        left, right = -1, len(nums)-1
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] < nums[mid+1]:   # 红色
                left = mid
            else:   # 蓝色
                right = mid
        return right
  • 时间O(logn),空间O(1)
153. 寻找旋转排序数组中的最小值

给你一个数组,它可能是一个递增的数组,也有可能是两段递增数组且第一个数 > 最后一个数 。如何用 O(logn) 的时间 找到数组的最小值

  • 需要一个判定方式来判断 numsmid(即二分的位置)是在最小值的左侧还是右侧
  • 可以和最后一个数比大小。由于最小值一定在数组中,那么最后一个数要么是最小值,要么在最小值的右侧。因此 n-1 一定是蓝色
  • 因此,在 0 ~ n-2 中二分
    • 如果 numsmid < 最后一个数,那么 numsmid 所处的位置有两种情况:在一段递增数组中,或者在两段递增数组中的第二段。无论是哪种情况,numsmid 要么是最小值,要么在最小值右侧。染成蓝色
    • 如果 numsmid > 最后一个数,那么 numsmid 只可能在两段递增数组中,且一定在最小值左侧(第一段)。染成红色
python 复制代码
class Solution:
    def findMin(self, nums: List[int]) -> int:
        # [0, n-2]
        # (-1, n-1)
        left, right = -1, len(nums)-1
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] > nums[-1]:   # 红色
                left = mid
            else:    # 蓝色
                right = mid
        return nums[right]
33. 搜索旋转排序数组

【力扣-Python-33】搜索旋转排序数组(middle)

找 target(可能不在数组中),需要0, n-1 上二分,有两种做法:

  • 参考153题,首先找到最小值,然后比较 target 和最后一个数的大小,来判断在哪段二分查找 target。需要两次二分
  • 可以只一次二分。分三种情况讨论,什么时候 numsmid 在 target 及其右侧(染成蓝色)
    • 如果二分的位置 > 最后一个数,说明在第一段。如果此时 target 也大于最后一个数,说明 target 也在第一段。且如果 numsmid >= target,说明在 target 及其右侧,染成蓝色
    • 如果二分的位置 ≤ 最后一个数,说明在第二段。如果此时 target 大于最后一个数,说明 target 在第一段。直接就说明 numsmid 在 target 及其右侧(染成蓝色)
    • 如果二分的位置 ≤ 最后一个数,说明在第二段。target 也在第二段,numsmid >= target,这种情况也是蓝色
  • 其余情况就是红色
python 复制代码
class Solution:
    def is_blue(self, nums, i, target):
        end = nums[-1]
        if nums[i] > end:
            return target > end and nums[i] >= target
        else:
            return target > end or nums[i] >= target

    # [0, n-1]
    # (-1, n)
    def search(self, nums: List[int], target: int) -> int:
        left, right = -1, len(nums)
        while left + 1 < right:
            mid = (left + right) // 2
            if self.is_blue(nums, mid, target):
                right = mid
            else:
                left = mid
        if right == len(nums) or nums[right] != target:
            return -1
        return right

课后作业

74. 搜索二维矩阵

【力扣-Python-74】搜索二维矩阵(middle)

1901. 寻找峰值 II

154. 寻找旋转排序数组中的最小值 II

相关推荐
风吹夏回6 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
kkeeper~6 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
小熊Coding6 小时前
Python爬取当当网二手图书项目实战!
开发语言·爬虫·python·beautifulsoup·requests·二手图书
秋96 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
小江的记录本6 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
2401_868534787 小时前
论企业网络设计
数据结构
田里的水稻7 小时前
OE_ubuntu26.04与宿主机之间复制粘贴内容
人工智能·python·机器人
wabs6668 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964138 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
jiayong238 小时前
02 创建虚拟环境
python