引言 二分查找是一种高效的查找算法,常用于在有序数组中快速定位特定元素。本文将深入探讨二分查找算法的基本原理、常见变体、应用场景以及在 LeetCode 上的实际应用。
1. 二分查找基本原理 二分查找的核心思想是通过比较目标值与数组中间元素的大小,不断缩小查找范围,直到找到目标值或确定目标值不存在。
基本步骤:
确定查找范围的左右边界
计算中间位置
将中间元素与目标值比较
根据比较结果缩小查找范围
重复步骤2-4,直到找到目标值或确定目标值不存在
2. 二分查找的基本实现 1 2 3 4 5 6 7 8 9 10 11 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
3. 二分查找的常见变体 3.1 查找第一个等于目标值的元素 1 2 3 4 5 6 7 8 9 10 11 12 13 def search_first_equal (nums, target ): left, right = 0 , len (nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: right = mid - 1 else : if mid == 0 or nums[mid - 1 ] < target: return mid right = mid - 1 return -1
3.2 查找最后一个等于目标值的元素 1 2 3 4 5 6 7 8 9 10 11 12 13 def search_last_equal (nums, target ): left, right = 0 , len (nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: right = mid - 1 else : if mid == len (nums) - 1 or nums[mid + 1 ] > target: return mid left = mid + 1 return -1
3.3 查找第一个大于等于目标值的元素 1 2 3 4 5 6 7 8 9 10 11 def search_first_greater_equal (nums, target ): left, right = 0 , len (nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 else : if mid == 0 or nums[mid - 1 ] < target: return mid right = mid - 1 return -1
3.4 查找最后一个小于等于目标值的元素 1 2 3 4 5 6 7 8 9 10 11 def search_last_less_equal (nums, target ): left, right = 0 , len (nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] > target: right = mid - 1 else : if mid == len (nums) - 1 or nums[mid + 1 ] > target: return mid left = mid + 1 return -1
4. LeetCode 实战应用 4.1 LeetCode 704. Binary Search 这是一个标准的二分查找问题。
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def search (self, nums: List [int ], target: int ) -> int : 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
4.2 LeetCode 35. Search Insert Position 这个问题要求我们在有序数组中找到目标值的索引,如果不存在,则返回它应该被插入的位置。
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def searchInsert (self, nums: List [int ], target: int ) -> int : 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
4.3 LeetCode 33. Search in Rotated Sorted Array 这个问题涉及在旋转排序数组中进行二分查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution : def search (self, nums: List [int ], target: int ) -> int : left, right = 0 , len (nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid if nums[left] <= nums[mid]: if nums[left] <= target < nums[mid]: right = mid - 1 else : left = mid + 1 else : if nums[mid] < target <= nums[right]: left = mid + 1 else : right = mid - 1 return -1
4.4 LeetCode 74. Search a 2D Matrix 这个问题要求在二维矩阵中进行二分查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution : def searchMatrix (self, matrix: List [List [int ]], target: int ) -> bool : 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 value = matrix[mid // n][mid % n] if value == target: return True elif value < target: left = mid + 1 else : right = mid - 1 return False
5. 二分查找的应用场景
有序数组查找 : 在有序数组中快速查找特定元素。
查找边界 : 寻找满足特定条件的元素的边界。
优化问题 : 在某些优化问题中,可以使用二分查找来快速逼近最优解。
数据库索引 : 在数据库索引结构中应用二分查找原理。
6. 二分查找的技巧与注意事项
边界条件处理 : 注意循环的终止条件,通常使用 left <= right
。
中点计算 : 使用 mid = left + (right - left) // 2
可以避免整数溢出。
死循环处理 : 确保在每次循环中,搜索范围都在缩小。
溢出问题 : 对于大数问题,注意处理可能的整数溢出。
精度问题 : 对于浮点数二分,需要考虑精度问题。
7. 二分查找的优化思路
预处理 : 对于某些问题,可以先对数据进行预处理,如排序,以便应用二分查找。
二分答案 : 对于一些判定问题,可以对答案进行二分查找。
多次二分 : 某些复杂问题可能需要多次应用二分查找。
结合其他算法 : 二分查找可以与其他算法(如双指针、滑动窗口等)结合使用。
8. 二分查找的局限性 尽管二分查找非常高效,但也存在一些局限性:
要求有序 : 二分查找要求数组必须是有序的,这可能需要额外的排序步骤。
不适用于动态数据 : 对于频繁插入和删除的数据结构,二分查找可能不太适用。
内存访问模式 : 在某些硬件架构上,二分查找的内存访问模式可能不如顺序访问高效。
小数据集 : 对于小数据集,简单的线性搜索可能更快。
结语 二分查找是一种强大而高效的算法,在处理有序数据时特别有用。通过本文的学习,我们深入了解了二分查找的基本原理、常见变体、LeetCode 实战应用以及各种技巧和注意事项。在实际应用中,要根据具体问题的特点,选择合适的二分查找策略,并结合其他算法技巧来优化解决方案。通过不断练习和总结,我们可以在算法解题中越来越熟练地运用二分查找,解决各种复杂的查找和优化问题。