深入理解 lower_bound() 和 upper_bound():C++二分查找利器
引言
在C++的算法世界中,lower_bound()和upper_bound()是两个极其有用的二分查找函数。它们是处理有序序列时的利器,能够在对数时间内完成查找操作。无论你是刷算法题还是进行实际开发,掌握这两个函数都能显著提升代码效率和优雅度。
函数概述
lower_bound()
// 在有序范围[first, last)中查找第一个不小于value的元素
template<class ForwardIt, class T>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value);
upper_bound()
// 在有序范围[first, last)中查找第一个大于value的元素
template<class ForwardIt, class T>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value);
核心区别
| 函数 | 查找条件 | 返回位置 |
|---|---|---|
lower_bound | 第一个 不小于 value 的元素 | value的插入点(不破坏顺序) |
upper_bound | 第一个 大于 value 的元素 | value的最后一个插入点 |
基础使用示例
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> nums = {1, 2, 4, 4, 5, 6, 7};
// lower_bound 示例
auto lb = std::lower_bound(nums.begin(), nums.end(), 4);
std::cout << "lower_bound for 4 at position: "
<< (lb - nums.begin()) << std::endl; // 输出: 2
// upper_bound 示例
auto ub = std::upper_bound(nums.begin(), nums.end(), 4);
std::cout << "upper_bound for 4 at position: "
<< (ub - nums.begin()) << std::endl; // 输出: 4
// 元素不存在的情况
auto lb5 = std::lower_bound(nums.begin(), nums.end(), 3);
std::cout << "lower_bound for 3 at position: "
<< (lb5 - nums.begin()) << std::endl; // 输出: 2
return 0;
}
实际应用场景
1. 查找元素是否存在(替代binary_search)
bool exists(const std::vector<int>& arr, int target) {
auto it = std::lower_bound(arr.begin(), arr.end(), target);
return it != arr.end() && *it == target;
}
2. 统计元素出现次数
int countOccurrences(const std::vector<int>& arr, int target) {
auto left = std::lower_bound(arr.begin(), arr.end(), target);
auto right = std::upper_bound(arr.begin(), arr.end(), target);
return std::distance(left, right); // 出现次数
}
3. 插入元素保持有序
void insertSorted(std::vector<int>& arr, int value) {
auto pos = std::lower_bound(arr.begin(), arr.end(), value);
arr.insert(pos, value);
}
4. 寻找范围区间
// 在有序数组中查找[L, R]范围内的所有元素
void findRange(const std::vector<int>& arr, int L, int R) {
auto start = std::lower_bound(arr.begin(), arr.end(), L);
auto end = std::upper_bound(arr.begin(), arr.end(), R);
for (auto it = start; it != end; ++it) {
std::cout << *it << " ";
}
}
自定义比较函数
对于复杂数据结构,可以自定义比较函数:
struct Person {
std::string name;
int age;
bool operator<(const Person& other) const {
return age < other.age;
}
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35},
{"David", 40}
};
// 查找年龄不小于32的人
Person target{"", 32};
auto it = std::lower_bound(people.begin(), people.end(), target,
[](const Person& a, const Person& b) {
return a.age < b.age;
});
if (it != people.end()) {
std::cout << "Found: " << it->name << ", " << it->age << std::endl;
}
return 0;
}
性能分析
时间复杂度
- 平均情况:O(log n),其中n是序列长度
- 相比线性查找的O(n),在处理大数据时优势明显
空间复杂度
- O(1),仅使用常数额外空间
常见错误与注意事项
-
容器必须有序
// 错误示例 std::vector<int> unsorted = {3, 1, 4, 1, 5}; auto it = std::lower_bound(unsorted.begin(), unsorted.end(), 3); // 未定义行为 -
检查返回的迭代器
// 正确做法 auto it = std::lower_bound(arr.begin(), arr.end(), target); if (it != arr.end() && *it == target) { // 找到元素 } -
处理重复元素
// 获取第一个等于value的位置 auto first = std::lower_bound(arr.begin(), arr.end(), value); // 获取最后一个等于value的位置 auto last = std::upper_bound(arr.begin(), arr.end(), value); // 注意:last指向的是第一个大于value的位置
算法题实战
LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return {-1, -1};
int left = lower_bound(nums.begin(), nums.end(), target) - nums.begin();
if (left == nums.size() || nums[left] != target) {
return {-1, -1};
}
int right = upper_bound(nums.begin(), nums.end(), target) - nums.begin() - 1;
return {left, right};
}
};
实现简单的二分查找
int binary_search(const std::vector<int>& arr, int target) {
int left = 0, right = arr.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return (left < arr.size() && arr[left] == target) ? left : -1;
}
进阶技巧
1. 在旋转排序数组中查找
int searchInRotatedArray(const std::vector<int>& nums, int target) {
int left = 0, right = nums.size();
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;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right - 1]) {
left = mid + 1;
} else {
right = mid;
}
}
}
return -1;
}
2. 查找峰值元素
int findPeakElement(const std::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;
}
记住,这两个函数的前提是序列必须有序,这是使用它们时的金科玉律。




3万+

被折叠的 条评论
为什么被折叠?



