二分查找是一个非常重要的算法技巧,常用于 有序数组 或范围内的搜索。对于 lower bound 和 upper bound,它们分别对应找到目标值的第一个位置和最后一个位置(或者一个范围之外的位置)。
基本定义
-
Lower Bound:
- 找到数组中 第一个大于等于目标值 的元素位置。
- 如果目标值存在,就是目标值的第一个位置;如果不存在,就是插入点的位置。
-
Upper Bound:
- 找到数组中 第一个大于目标值 的元素位置。
- 如果目标值存在,它指向目标值的最后一个位置的下一位;如果不存在,也是插入点的位置。
示例问题
给定有序数组 nums = [1, 3, 3, 5, 7] 和目标值 target = 3:
- Lower Bound: 应返回索引
1(第一个3的位置)。 - Upper Bound: 应返回索引
3(第一个大于3的位置,即5)。
实现代码
Lower Bound (第一个大于等于目标值的位置)
int lowerBound(vector<int>& nums, int target) {
int left = 0, right = nums.size(); // [left, right)
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1; // 排除小于 target 的部分
} else {
right = mid; // 包含 >= target 的部分
}
}
return left; // 此时 left == right
}
Upper Bound (第一个大于目标值的位置)
int upperBound(vector<int>& nums, int target) {
int left = 0, right = nums.size(); // [left, right)
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1; // 排除 <= target 的部分
} else {
right = mid; // 包含 > target 的部分
}
}
return left; // 此时 left == right
}
标准库 std::lower_bound 和 std::upper_bound
C++ STL 中提供了这两个功能,定义在 <algorithm> 中。
使用方法
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> nums = {1, 3, 3, 5, 7};
int target = 3;
// Lower Bound
int lb = lower_bound(nums.begin(), nums.end(), target) - nums.begin();
cout << "Lower Bound: " << lb << endl; // 输出 1
// Upper Bound
int ub = upper_bound(nums.begin(), nums.end(), target) - nums.begin();
cout << "Upper Bound: " << ub << endl; // 输出 3
return 0;
}
注意
std::lower_bound返回的是第一个大于等于target的迭代器。如果所有元素都小于target,upper_bound会返回nums.end()。std::upper_bound返回的是第一个大于target的迭代器。所有元素都小于target,返回nums.end()。
比较 Lower Bound 和 Upper Bound
| 类型 | 目标 | 返回值 |
|---|---|---|
lower_bound | >= target | 第一个大于等于目标值的位置。 |
upper_bound | > target | 第一个严格大于目标值的位置。 |
应用场景
-
查找范围:
- 找出所有等于
target的元素。 - 范围是
[lower_bound, upper_bound)。
auto start = lower_bound(nums.begin(), nums.end(), target); auto end = upper_bound(nums.begin(), nums.end(), target); cout << "Count: " << end - start << endl; // 范围内元素个数 - 找出所有等于
-
插入位置:
lower_bound可用于找插入位置,使数组仍然有序。
-
搜索问题:
- 在某些区间问题中使用
upper_bound或lower_bound来缩小范围。
- 在某些区间问题中使用
总结
lower_bound和upper_bound的核心在于二分查找。lower_bound寻找第一个大于等于目标值的位置,upper_bound寻找第一个大于目标值的位置。- 二者结合,可以快速解决范围查询或有序插入问题。
以下是一些与 二分查找、lower bound 和 upper bound 相关的经典 LeetCode 题目。这些题目中,有的直接用到了 lower_bound 或 upper_bound 的思想,有的则是二分查找的变种应用。
经典二分查找类问题
1. 704. Binary Search
- 描述: 在一个有序数组中找到目标值,返回其索引。如果不存在,返回 -1。
- 关键词: 基本的二分查找。
- LeetCode 704
2. 35. Search Insert Position
- 描述: 找到目标值在数组中的插入位置。如果目标值存在,返回其索引;否则返回插入点。
- 关键词:
lower_bound应用。 - LeetCode 35
3. 34. Find First and Last Position of Element in Sorted Array
- 描述: 找到目标值在数组中的起始和结束位置。如果不存在,返回 [-1, -1]。
- 关键词:
lower_bound和upper_bound的结合应用。 - LeetCode 34
meta vo 变种题
sorted array 中,是否有超过 n/3 的数
for(int i = 0; i < nums.size(); i+= n/3){
//用upper_bound, lower_bound 检查是否超过n/3 超过 返回true 否则 返回false
}
4. 2363. Merge Similar Items(Bloomberg VO)
- 描述: 合并两个包含键值对的数组,将权重相加,并按键值升序返回结果。
- 关键词: 哈希表存储合并权重,排序生成结果。
- LeetCode 2363
5. 69. Sqrt(x)
- 描述: 实现整数平方根函数,返回小于等于平方根的最大整数值。
- 关键词: 二分查找在数学问题中的应用。
- LeetCode 69
6. 374. Guess Number Higher or Lower
- 描述: 猜数字游戏,目标是通过调用 API 找到目标值。
- 关键词: 基础二分查找。
- LeetCode 374
高级二分查找及其变种
7. 162. Find Peak Element
- 描述: 找到一个峰值元素,峰值满足
nums[i] > nums[i-1]且nums[i] > nums[i+1]。 - 关键词: 二分查找的条件变化。
- LeetCode 162
8. 153. Find Minimum in Rotated Sorted Array
- 描述: 在旋转排序数组中找到最小值。
- 关键词: 二分查找在旋转数组中的应用。
- LeetCode 153
9. 154. Find Minimum in Rotated Sorted Array II
- 描述: 在旋转排序数组中找到最小值,数组中允许存在重复值。
- 关键词: 二分查找处理边界条件。
- LeetCode 154
10. 33. Search in Rotated Sorted Array
- 描述: 在旋转排序数组中找到目标值,返回其索引。如果不存在,返回 -1。
- 关键词: 二分查找处理旋转。
- LeetCode 33
11. 658. Find K Closest Elements
- 描述: 在数组中找到与目标值最接近的 k 个元素,返回排序后的结果。
- 关键词: 二分查找范围。
- LeetCode 658
数学与区间问题结合的二分查找
11. 410. Split Array Largest Sum
- 描述: 将数组分成
m段,使得段内和的最大值最小化。 - 关键词: 二分查找 + 贪心。
- LeetCode 410
12. 287. Find the Duplicate Number
- 描述: 在一个范围为
[1, n]的数组中找到重复的数字。 - 关键词: 二分查找用于抽象范围,而非数组本身。
- LeetCode 287
13. 378. Kth Smallest Element in a Sorted Matrix
- 描述: 找到排序矩阵中的第 k 小元素。
- 关键词: 二分查找范围。
- LeetCode 378
14. 302. Smallest Rectangle Enclosing Black Pixels
- 描述: 在二维数组中找到包含所有黑色像素的最小矩形。
- 关键词: 二分查找在二维范围的应用。
- LeetCode 302
总结
- 基础二分查找问题:例如
704和35,专注于实现标准的二分查找。 lower_bound和upper_bound问题:例如34和35,处理范围和插入点问题。- 高级应用:例如旋转数组和峰值问题,考察对条件的灵活处理。
- 范围缩小问题:例如
410和287,将数组的二分思想扩展到值或区间。