二分查找基础概念
二分查找(Binary Search)是一种在有序数组中查找特定元素的高效算法,时间复杂度为O(log n)。其核心思想是通过不断将搜索范围减半来快速定位目标值。
二分查找的实现
标准二分查找的实现通常包括以下步骤:
- 初始化左右边界
left
和right
,通常为数组的起始和结束索引。 - 计算中间索引
mid = left + (right - left) / 2
(避免溢出)。 - 比较中间元素与目标值,调整左右边界缩小搜索范围。
int binarySearch(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1; // 未找到
}
二分查找的变种
-
查找第一个等于目标值的索引
适用于有重复元素的情况,需检查左侧元素是否仍等于目标值。int findFirst(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] >= target) right = mid - 1; else left = mid + 1; } return (left < nums.size() && nums[left] == target) ? left : -1; }
-
查找最后一个等于目标值的索引
检查右侧元素是否仍等于目标值。int findLast(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] <= target) left = mid + 1; else right = mid - 1; } return (right >= 0 && nums[right] == target) ? right : -1; }
-
查找第一个大于等于目标值的索引
可用于插入位置问题。int lowerBound(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] >= target) right = mid - 1; else left = mid + 1; } return left; }
-
查找第一个大于目标值的索引
int upperBound(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] > target) right = mid - 1; else left = mid + 1; } return left; }
二分查找的边界条件
- 循环条件:
left <= right
确保所有元素被检查。 - 中间值计算:使用
mid = left + (right - left) / 2
避免溢出。 - 边界更新:根据比较结果更新
left
或right
。
二分查找的例题
-
搜索插入位置
给定有序数组和目标值,返回目标值应插入的索引。int searchInsert(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) return mid; if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return left; }
-
在旋转排序数组中搜索
数组可能在某点旋转,但仍部分有序。int searchRotated(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) return mid; if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) right = mid - 1; else left = mid + 1; } else { if (nums[mid] < target && target <= nums[right]) left = mid + 1; else right = mid - 1; } } return -1; }
-
寻找峰值元素
峰值元素大于其相邻元素。int findPeakElement(vector<int>& nums) { int left = 0, right = nums.size() - 1; while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] < nums[mid + 1]) left = mid + 1; else right = mid; } return left; }
-
在排序数组中查找元素的第一个和最后一个位置
结合变种二分查找实现。vector<int> searchRange(vector<int>& nums, int target) { int first = findFirst(nums, target); int last = findLast(nums, target); return {first, last}; }
-
x的平方根
使用二分法近似计算平方根。int mySqrt(int x) { if (x <= 1) return x; int left = 1, right = x; while (left <= right) { int mid = left + (right - left) / 2; if (mid == x / mid) return mid; if (mid < x / mid) left = mid + 1; else right = mid - 1; } return right; }
二分查找的应用场景
- 有序或部分有序数组的查找。
- 需要快速定位边界或极值的问题。
- 数学近似计算(如平方根)。
注意事项
- 确保数组有序或部分有序。
- 注意边界条件的处理,避免死循环或越界。
- 根据问题需求选择合适的变种实现。