1. 题目解析与核心思路
Leetcode 143题实际上包含两个经典算法问题:搜索插入位置(Search Insert Position)和搜索二维矩阵(Search a 2D Matrix)。这两个问题看似不同,但核心都考察二分查找算法的灵活应用能力。
1.1 搜索插入位置问题
给定一个排序数组和一个目标值,要求在数组中找到目标值的位置。如果目标值不存在,则返回它将会被按顺序插入的位置。例如:
- 输入: nums = [1,3,5,6], target = 5 → 输出: 2
- 输入: nums = [1,3,5,6], target = 2 → 输出: 1
1.2 搜索二维矩阵问题
给定一个m×n的矩阵,其中每行的元素从左到右升序排列,每列的元素从上到下升序排列。要求判断目标值是否存在于矩阵中。例如:
[ [1, 4, 7, 11], [2, 5, 8, 12], [3, 6, 9, 16] ]- 输入: target = 5 → 输出: true
- 输入: target = 10 → 输出: false
2. 二分查找算法精讲
2.1 标准二分查找实现
二分查找的核心在于每次将搜索范围减半。标准实现需要注意三个关键点:
def binary_search(nums, target): left, right = 0, len(nums) - 1 while left <= right: mid = left + (right - left) // 2 # 防止溢出 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return -1 # 未找到注意:计算mid时使用
left + (right - left) // 2而非(left + right) // 2,可以避免整数溢出问题。
2.2 变种:搜索插入位置
搜索插入位置是二分查找的变种,关键在于处理未找到目标值时返回left指针:
def search_insert(nums, target): left, right = 0, len(nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return left # 关键区别2.3 时间复杂度分析
二分查找的时间复杂度为O(log n),空间复杂度为O(1)。对于m×n的二维矩阵,如果采用两次二分查找(先行后列),时间复杂度为O(log m + log n)。
3. 二维矩阵搜索的三种解法
3.1 两次二分查找法
先对第一列进行二分查找确定行,再在该行进行二分查找:
def searchMatrix(matrix, target): if not matrix or not matrix[0]: return False # 在第一列中查找合适的行 row = bisect.bisect_right([row[0] for row in matrix], target) - 1 if row < 0: return False # 在选定的行中进行二分查找 col = bisect.bisect_left(matrix[row], target) return col < len(matrix[row]) and matrix[row][col] == target3.2 全局二分查找法
将二维矩阵视为一维数组进行二分查找:
def searchMatrix(matrix, target): if not matrix or not matrix[0]: return False m, n = len(matrix), len(matrix[0]) left, right = 0, m * n - 1 while left <= right: mid = left + (right - left) // 2 num = matrix[mid // n][mid % n] if num == target: return True elif num < target: left = mid + 1 else: right = mid - 1 return False3.3 步进搜索法
从矩阵右上角开始,逐步向左下角移动:
def searchMatrix(matrix, target): if not matrix or not matrix[0]: return False row, col = 0, len(matrix[0]) - 1 while row < len(matrix) and col >= 0: if matrix[row][col] == target: return True elif matrix[row][col] < target: row += 1 else: col -= 1 return False4. 边界条件与常见错误
4.1 空输入处理
必须考虑矩阵为空或矩阵行/列为空的情况:
if not matrix or not matrix[0]: return False # 或适当返回值4.2 整数溢出问题
计算mid时常见的错误写法:
mid = (left + right) // 2 # 可能溢出应改为:
mid = left + (right - left) // 24.3 循环终止条件
while循环的条件应为left <= right而非left < right,否则可能漏判边界情况。
5. 性能优化技巧
5.1 提前终止
在步进搜索法中,一旦发现当前元素大于目标值且是行首元素,或小于目标值且是列尾元素,可以立即终止搜索。
5.2 缓存友好访问
在全局二分查找法中,按行优先顺序访问元素比列优先更高效,因为现代计算机的缓存机制对连续内存访问更友好。
5.3 分支预测优化
在二分查找的核心循环中,将相等判断放在最前面可能提高分支预测成功率:
if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 16. 实际应用场景
6.1 数据库索引查找
二分查找是B+树等数据库索引结构的核心算法,理解其变种对优化查询性能至关重要。
6.2 游戏中的碰撞检测
在2D游戏中,对排序后的物体坐标使用二分查找可以快速定位可能发生碰撞的对象。
6.3 实时日志分析
处理按时间排序的日志数据时,二分查找可以快速定位特定时间范围内的事件。
7. 扩展练习建议
- Leetcode 34:在排序数组中查找元素的第一个和最后一个位置
- Leetcode 240:搜索二维矩阵 II(每行升序+每列升序)
- Leetcode 378:有序矩阵中第K小的元素
- Leetcode 702:搜索长度未知的有序数组
在实际编码面试中,面试官常常会基于这些基础问题进行变种考察。建议先彻底掌握标准二分查找的实现,再逐步挑战各种变种问题。