二分查找基础原理与题目说明

二分查找基础原理与题目说明

文章目录

🔗 查看完整专栏(LeetCode基础算法专栏

点击阅读:Python 数据结构与语法速查笔记

点击阅读:哈希表基础原理与题目说明

点击阅读:双指针基础原理与题目说明

点击阅读:滑动窗口基础原理与题目说明

点击阅读:队列与单调队列基础原理与题目说明

点击阅读:二分查找基础原理与题目说明

点击阅读:矩阵基础原理与题目说明

特别说明:

本文为个人的 LeetCode 刷题与学习笔记,内容仅供学习与交流使用,禁止转载或用于商业用途。需要强调的是,文中的题目解法不一定是最优解(可能存在时间或空间复杂度的进一步优化空间),主要目的是分享个人的解题思路与逻辑实现,仅供参考。 笔记内容为个人理解与总结,可能存在疏漏或偏差,欢迎读者自行甄别并交流探讨。

一、 什么是二分查找?

二分查找(Binary Search)是基于分治(减治)思想 的高效查找算法。针对有序区间,通过「每轮排除一半搜索空间」的方式,将查找效率从线性查找的 O ( n ) O(n) O(n) 飞跃提升到 O ( log ⁡ n ) O(\log n) O(logn)。

核心前提:

  1. 数据有序(或具有某种可以明确切分的局部有序性/单调性)。
  2. 支持随机访问(底层必须是数组,链表等无法直接通过索引访问的结构不适用)。

二、 经典二分查找应用

35. 搜索插入位置

题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。要求 O ( log ⁡ n ) O(\log n) O(logn) 的时间复杂度。

解题思路

标准的**「找最左侧答案 / 插入位置」**模板题。

初始化左闭右开区间 [0, len(nums)),这样能完美覆盖 target 大于所有元素需要插入到数组末尾的情况。每次判断 nums[mid] < target,若是则 left = mid + 1,否则 right = mid,最终 left 就是插入位置。

核心代码

py 复制代码
from typing import List

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)  # 左闭右开区间
        
        while left < right:
            mid = (left + right) // 2

            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1      # 目标在右侧,跳过 mid
            else:
                right = mid         # 目标在左侧,保留 mid 作为候选插入点
        
        # 循环结束时 left == right
        return left

34. 在排序数组中查找元素的第一个和最后一个位置

题目描述

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果不存在,返回 [-1, -1]

解题思路

这道题是左/右边界模板的完美结合

  1. 第一次二分 :找第一个 ≥ t a r g e t \ge target ≥target 的位置(左边界),直接套用「找最左侧」模板。
  2. 第二次二分 :找最后一个 ≤ t a r g e t \le target ≤target 的位置(右边界),直接套用「找最右侧」模板。
  3. 最后通过验证边界的合法性与元素匹配性,判断目标值是否存在。

核心代码

py 复制代码
from typing import List

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if not nums:
            return [-1, -1]

        # 1. 找第一个 >= target 的数值 ------ 锁定最左边界
        left = 0
        right = len(nums)
        while left < right:
            mid = (left + right) // 2
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid
        start_idx = left

        # 2. 找最后一个 <= target 的数值 ------ 锁定最右边界
        left = 0
        right = len(nums) - 1
        while left < right:
            mid = (left + right + 1) // 2  # 必须 +1 防死循环
            if nums[mid] > target:
                right = mid - 1
            else:
                left = mid
        end_idx = left

        # 3. 校验最终找到的区间是否合法且匹配 target
        if start_idx > end_idx or start_idx >= len(nums) or nums[end_idx] != target:
            return [-1, -1]

        return [start_idx, end_idx]

三、 二维矩阵中的二分查找

74. 搜索二维矩阵

题目描述

给你一个 m × n m \times n m×n 整数矩阵。每行从左到右递增,且每行的第一个整数大于前一行的最后一个整数。判断目标值是否存在于矩阵中。

解题思路

选用两次二分查找解题,充分利用矩阵特性。

  1. 第一次二分 :定位 target 可能所在的行。本质是找最后一个行首 ≤ t a r g e t \le target ≤target 的行,这属于「找最右侧答案」,套用对应模板。
  2. 第二次二分 :在目标行内判断目标值是否存在,这属于普通的二分查找判定。整体时间复杂度 O ( log ⁡ ( m ) + log ⁡ ( n ) ) = O ( log ⁡ ( m n ) ) O(\log(m) + \log(n)) = O(\log(mn)) O(log(m)+log(n))=O(log(mn))。

核心代码

py 复制代码
from typing import List

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        # 1. 第一次二分:定位 target 所在的行
        left = 0
        right = len(matrix) - 1

        while left < right:
            mid = (left + right + 1) // 2  # 找最右侧满足条件的行,加1防死循环
            if matrix[mid][0] > target:
                right = mid - 1
            else:
                left = mid
        
        row = left  # 锁定目标行

        # 2. 第二次二分:在目标行中查找 target
        left = 0
        right = len(matrix[0]) - 1  # 此处采用精确匹配的闭区间写法

        while left <= right:
            mid = (left + right) // 2
            if matrix[row][mid] == target:
                return True
            elif matrix[row][mid] < target:
                left = mid + 1
            else:
                right = mid - 1
                
        return False

240. 搜索二维矩阵 II

题目描述

矩阵每行从左到右升序,每列从上到下升序。搜索目标值。

解题思路

注:本题有从右上角出发 O ( m + n ) O(m+n) O(m+n) 的解法,此处分享基于二分思想的解法。

逐行遍历,由于每一行都是严格升序的,可以直接对每一行套用标准的「左闭右闭」一维二分模板。虽然不是最优的时间复杂度,但逻辑清晰、健壮。

核心代码

py 复制代码
from typing import List

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix or not matrix[0]:
            return False

        n, m = len(matrix), len(matrix[0])

        for i in range(n):
            left, right = 0, m - 1
            while left <= right:
                mid = (left + right) // 2
                if matrix[i][mid] == target:
                    return True
                elif matrix[i][mid] < target:
                    left = mid + 1          
                else:
                    right = mid - 1         
        return False

四、 旋转排序数组系列

旋转数组打破了全局的有序性,但保留了局部有序性 。二分的难点在于如何通过 mid 判断目标值落在左半段还是右半段。

33. 搜索旋转排序数组

题目描述

整数数组 nums 按升序排列且互不相同,在某个下标上进行了旋转。求目标值 target 的下标。要求 O ( log ⁡ n ) O(\log n) O(logn) 复杂度。

解题思路

原数组旋转后,通过 mid 切开,必定有一半区间是严格升序的

  1. 判断哪一半有序 :通过判断 nums[left] <= nums[mid],若成立,说明 [left, mid] 有序;否则 [mid, right] 有序。
  2. 判断 target 是否在有序区间内:如果在有序的那一半区间内,直接将边界收缩到该区间;如果不在,则说明在另一半乱序区间内,收缩到另一半。

核心代码

py 复制代码
from typing import List

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

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

            if nums[mid] == target:
                return mid
                
            # 1. 左半区间 [left, mid] 是严格有序的
            if nums[left] <= nums[mid]:
                # 检查 target 是否在有序的左半区间内
                if nums[left] <= target < nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            # 2. 否则,右半区间 [mid, right] 是严格有序的
            else:
                # 检查 target 是否在有序的右半区间内
                if nums[mid] < target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1
            
        return -1

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

题目描述

找出并返回旋转后升序数组中的最小元素。要求 O ( log ⁡ n ) O(\log n) O(logn)。

解题思路

旋转后数组被分为两段连续的升序子数组,且前一段的所有元素必定大于后一段的所有元素。最小值就是这两段的分界点。

我们将 nums[mid] 与右边界 nums[right] 进行比较:

  • nums[mid] < nums[right]:说明右侧是严格递增的,最小值一定在左侧或就是 mid,故 right = mid
  • nums[mid] > nums[right]:说明出现了断层,最小值一定在 mid 右侧,故 left = mid + 1

核心代码

py 复制代码
from typing import List

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

        while left < right:
            mid = (left + right) // 2
            
            # 若 mid 小于最右侧元素,说明 mid 到 right 是递增的
            # 最小值必然在 left 到 mid 之间
            if nums[mid] < nums[right]:
                right = mid
            # 若 mid 大于最右侧,说明断层在右边
            else:
                left = mid + 1
                
        return nums[left]
相关推荐
算法鑫探18 小时前
闰年判断:C语言实战解析
c语言·数据结构·算法·新人首发
yaoxin52112318 小时前
384. Java IO API - Java 文件复制工具:Copy 示例完整解析
java·开发语言·python
Greyson118 小时前
Layui表格如何使用第三方插件实现树形展示.txt
jvm·数据库·python
WBluuue18 小时前
数据结构与算法:康托展开、约瑟夫环、完美洗牌
c++·算法
2401_8716965218 小时前
mysql行级锁失效的原因排查_检查查询条件与执行计划
jvm·数据库·python
NotFound48618 小时前
实战指南如何实现Java Web 拦截机制:Filter 与 Interceptor 深度分享
java·开发语言·前端
木子墨51618 小时前
LeetCode 热题 100 精讲 | 并查集篇:最长连续序列 · 岛屿数量 · 省份数量 · 冗余连接 · 等式方程的可满足性
数据结构·c++·算法·leetcode
xzal1218 小时前
python中,turtle基础知识笔记1
笔记·python·turtle
Rubin智造社18 小时前
安全先行·自主编程|Claude Code Opus 4.7深度解读:AI开发进入合规量产时代
人工智能·anthropic·claude opus 4.7·mythos preview·xhigh努力等级·/ultrareview命令·自主开发ai