← 目录 / 第十一章 · STL 标准模板库 / 11.13.2 查找与计数

11.13.2 查找与计数

find、count、max_element / min_element、binary_search、lower_bound / upper_bound —— 在数据里找东西、数东西的通用算法。

📂 引入头文件:#include <algorithm> —— 本节算法全部在这里。
本页目录
① find —— 查找元素

从前往后逐个比较,找到第一个等于目标值的元素,返回指向它的迭代器;如果找不到,返回 end()。时间复杂度 O(n),适用于任意容器(包括无序的)。

C++ · find 查找单个元素
1vector<int> v = {3, 1, 4, 1, 5};
2auto it = find(v.begin(), v.end(), 4);
3if (it != v.end())
4{
5 cout << "找到了: " << *it << endl; // 找到了: 4
6 cout << "下标: " << (it - v.begin()) << endl; // 下标: 2
7}
8
9// 找不到时返回 end()
10auto it2 = find(v.begin(), v.end(), 99);
11if (it2 == v.end()) {
12 cout << "没找到 99" << endl;
13}
📌
判断"是否找到"的标准写法:永远是和 end() 比较(it != v.end()),不要直接判断迭代器是否为"空"——迭代器没有像指针那样天然的"空值"概念。
② count —— 统计出现次数

统计某个值在区间内出现了多少次。

C++ · count 统计次数
1vector<int> v = {1, 2, 3, 2, 2, 4};
2int cnt = count(v.begin(), v.end(), 2);
3cout << "2 出现了 " << cnt << " 次" << endl; // 2 出现了 3 次
🚀
对于 set / map,优先用它们自己的成员函数:上面的 findcount通用算法,对任何容器都适用,但代价是只能"逐个遍历比较",时间复杂度 O(n)。setmap 这类关联容器内部是有序结构(红黑树),自带了效率更高的成员函数 .find().count(),时间复杂度是 O(log n)。也就是说,find(s.begin(), s.end(), x)s.find(x) 效果一样,但后者快得多——用 set/map 时,永远优先用它们自己的成员函数,而不是这里的通用算法。
③ max_element / min_element —— 找最大/最小值的位置

注意这两个函数返回的是迭代器(位置),不是值本身——需要解引用 *it 才能拿到具体的值。

C++ · 找最大值和最小值
1vector<int> v = {3, 1, 4, 1, 5, 9};
2
3auto max_it = max_element(v.begin(), v.end());
4cout << "最大值: " << *max_it << endl; // 最大值: 9
5cout << "下标: " << (max_it - v.begin()) << endl; // 下标: 5
6
7auto min_it = min_element(v.begin(), v.end());
8cout << "最小值: " << *min_it << endl; // 最小值: 1
📌
如果只想要值,不需要下标,可以直接写 *max_element(v.begin(), v.end()),一行解决——但每次调用都是 O(n) 重新扫描一遍,循环里频繁调用要注意效率。

与前面的 find(O(n),逐个遍历比较)不同,binary_search 系列算法要求数据已经排好序,但作为交换条件,能以 O(log n) 的速度完成查找——数据量大的时候,这个速度差距会非常明显(比如 n=100万 时,O(n) 要比较约 100 万次,O(log n) 只需要约 20 次)。它只告诉你"有没有",返回一个 bool,不会告诉你具体在哪个位置——如果需要位置,要用下面的 lower_bound

C++ · binary_search 判断元素是否存在
1vector<int> v = {1, 3, 5, 7, 9};
2
3if (binary_search(v.begin(), v.end(), 5))
4{
5 cout << "5 在数组中" << endl;
6}
7
8if (!binary_search(v.begin(), v.end(), 4))
9{
10 cout << "4 不在数组中" << endl;
11}
⑤ lower_bound / upper_bound —— 二分查找位置

同样只能在已排序的序列上使用,时间复杂度 O(log n)。和 binary_search 不同,它们返回的是位置(迭代器):

函数含义找的是
lower_bound第一个 ≥ target 的位置下界(包含等于)
upper_bound第一个 > target 的位置上界(不包含等于)
v = {1, 2, 2, 2, 3, 4};查找 target = 2
 
1
0
lower_bound
2
1
 
2
2
 
2
3
upper_bound
3
4
 
4
5
lower_bound 停在下标 1(第一个 ≥ 2 的位置),upper_bound 停在下标 4(第一个 > 2 的位置)。两者之间的区间 [lower_bound, upper_bound) 正好就是所有等于 2 的元素,区间长度 = 2 出现的次数。
C++ · lower_bound 与 upper_bound
1vector<int> v = {1, 2, 2, 2, 3, 4};
2
3// lower_bound:第一个 ≥ target 的位置
4auto lb = lower_bound(v.begin(), v.end(), 2);
5cout << "第一个 ≥ 2 的下标: " << (lb - v.begin()) << endl; // 输出: 1
6
7// upper_bound:第一个 > target 的位置
8auto ub = upper_bound(v.begin(), v.end(), 2);
9cout << "第一个 > 2 的下标: " << (ub - v.begin()) << endl; // 输出: 4
10
11// 等于 2 的元素个数
12cout << "2 的个数: " << (ub - lb) << endl; // 输出: 3
🎯
竞赛中 lower_boundfind 常用得多——对于有序数组,O(log n) 比 O(n) 快太多。mapset 这些关联容器也有自己的成员函数版本 .lower_bound(),效果一样但效率更高(不需要重新排序)。
⚠️
注意 end() 这个边界情况:如果要查找的 target 比序列里所有元素都大,lower_bound(以及 upper_bound)会返回 end()——这代表"没有任何元素满足条件"。end() 并不指向一个真实存在的元素,在计算下标或者用 *it 解引用之前,一定要先判断 it != v.end(),否则解引用 end() 是未定义行为,很容易导致程序崩溃或者读到一堆垃圾值。