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

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

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


二分查找

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

04 二分查找 红蓝染色法

课程讲解

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

要求 nums 是非递减的,即 nums[i] <= nums[i+1],返回最小的满足 nums[i] >= 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) 的时间 找到数组的最小值

  • 需要一个判定方式来判断 nums[mid](即二分的位置)是在最小值的左侧还是右侧
  • 可以和最后一个数比大小。由于最小值一定在数组中,那么最后一个数要么是最小值,要么在最小值的右侧。因此 n-1 一定是蓝色
  • 因此,在 0 ~ n-2 中二分
    • 如果 nums[mid] < 最后一个数,那么 nums[mid] 所处的位置有两种情况:在一段递增数组中,或者在两段递增数组中的第二段。无论是哪种情况,nums[mid] 要么是最小值,要么在最小值右侧。染成蓝色
    • 如果 nums[mid] > 最后一个数,那么 nums[mid] 只可能在两段递增数组中,且一定在最小值左侧(第一段)。染成红色
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。需要两次二分
  • 可以只一次二分。分三种情况讨论,什么时候 nums[mid] 在 target 及其右侧(染成蓝色)
    • 如果二分的位置 > 最后一个数,说明在第一段。如果此时 target 也大于最后一个数,说明 target 也在第一段。且如果 nums[mid] >= target,说明在 target 及其右侧,染成蓝色
    • 如果二分的位置 ≤ 最后一个数,说明在第二段。如果此时 target 大于最后一个数,说明 target 在第一段。直接就说明 nums[mid] 在 target 及其右侧(染成蓝色)
    • 如果二分的位置 ≤ 最后一个数,说明在第二段。target 也在第二段,nums[mid] >= 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

相关推荐
Funny_AI_LAB1 小时前
Naval最新播客谈“氛围编码”:Vibe Coding 开启“一人独角兽”时代
人工智能·算法·语言模型·agi
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月3日
大数据·人工智能·python·信息可视化·自然语言处理
我不是懒洋洋2 小时前
【数据结构】排序算法(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)
c语言·数据结构·c++·经验分享·算法·排序算法
MediaTea2 小时前
ML:逻辑回归的基本原理与实现
人工智能·算法·机器学习·数据挖掘·逻辑回归
西红柿炒番茄312 小时前
【Python】一个自动切换壁纸的python程序
开发语言·python
辛苦才能2 小时前
数据结构--排序--插入排序(C语言,重点排序面试和比赛都会考察)
c语言·数据结构·面试
跨境数据猎手2 小时前
B 站 item_search_video 接口开发,搭建生产级视频搜索服务
大数据·爬虫·python
EnCi Zheng2 小时前
03ab-PyTorch安装教程 [特殊字符]
人工智能·pytorch·python
SmartBrain2 小时前
从Prompt工程到Harness工程:AI Agent落地之路
人工智能·python·华为·aigc