1 找到区间中某个值
1.1 区间未排序
想知道list容器中是否存在某个特定的Widget对象w。使用count:
list<Widget> lw;
Widget w;
...
if (count(lw.begin(), lw.end(), w) != 0) {
// w在lw中
...
} else {
// w不在lw中
...
}
count返回0表示没找到,>0表示找到了元素。
使用find:
if (find(lw.begin(), lw.end(), w) != lw.end()) {
...
} else {
...
}
需要测试find的返回值是否等于链表的end迭代器。find找到第一个匹配结果后马上就返回了,而count必须要到底区间的末尾。
如果我们不仅要知道一个值是否存在,而且要知道哪个对象时,需要使用find:
list<Widget>::iterator i = find(lw.begin(), lw.end(), w);
if (i != lw.end()) {
// 找到了,i指向第一个匹配的对象
...
} else {
...
}
1.2 区间排序
使用查找算法,binary_search、lower_bound、upper_bound和equal_range是以对数时间运行的。
1 binary_search
测试排序区间中是否存在某个特定的值:
vector<Widget> vw;
...
// 对vector进行排序
sort(vw.begin(), vw.end());
// 待查找的值
Widget w;
...
if (binary_search(vw.begin(), vw.end(), w)) {
// w在vw中
...
} else {
// w不在vw中
...
}
2 lower_bound
定位区间中的对象,当用lower_bound来查找某个特定值时,它会返回一个迭代器,迭代器要么指向该值的第一份副本(找到的话),要么指向一个适合于插入该值的位置(没找到的话)。所以,你必须测试lower_bound返回的迭代器,以判断该对象是否是你想要找的值。
许多程序员会按如下方式使用lower_bound:
vector<Widget>::iterator i = lower_bound(vw.begin(), vw.end(), w);
// 这里有个错误!
if (i != vw.end() && *i == w) {
...
} else {
...
}
上面if里是个相等性测试,但lower_bound使用等价性来搜索的。大多数情况下等价性测试和相等性测试结果是相同的,但19条也说明了不同的情况。
3 equal_range
定位区间中的对象,使用equal_range。equal_range返回一对迭代器,第一个迭代器等于lower_bound返回的迭代器,第二个迭代器等于upper_bound返回的迭代器。equal_range返回的这一对迭代器标识了一个子区间,其中的值与你所查找的值等价。
(1)如果返回的两个迭代器相同,则说明查找所得的对象区间为空:即没有找到这样的值
vector<Widget> vw;
...
sort(vw.begin(), vw.end());
typedef vector<Widget>::iterator VWIter;
typedef pare<VWIter, VWIter> VWIterPair;
VWIterPair p = equal_range(vw.begin(), vw.end(), w);
if (p.first != p.second) {
// 找到了特定值,p.first指向了第一个与w等价的对象,p.second指向最后一个与w等价的对象的下一个w位置
...
} else {
// 没找到特定值,p.first和p.second都指向w的插入位置
...
}
这段代码只使用了等价性,所以总是正确的。
(2)equal_range返回的迭代器之间的距离与这个区间中的对象数目是相等的
例如,如果要在vw中找到与w等价的Widget对象的位置,并打印出有多少个这样的对象:
VWIterPair p = equal_range(vw.begin(), vw.end(), w);
cout << "There are " << distance(p.first, p.second) << " elements in vw equivalent to w";
2 找到区间中某个位置
到目前为止,我们要在一个区间中查找某个特定的值,但有时我们需要找到区间中的某个位置。
例如,假设我们有一个Timestamp类和一个存放Timestamp的vector,且vector已经排序,其中老的时间戳在前面(即时间小的值在前面,时间大的值在后面):
class Timestamp {...};
// 判断lhs是否在rhs之前
bool operator<(const Timestamp& lhs, const Timestamp& rhs);
vector<Timestamp> vt;
...
sort(vt.begin(), vt.end());
我们希望从vt中删除比ageLimit这个时间戳值小的元素,即需要找到>=ageLimit值的位置:
Timestamp ageLimit;
...
// 从vt中删除所有在ageLimit之前的对象
vt.erase(vt.begin(), lower_bound(vt.begin(), vt.end(), ageLimit));
我们希望删除比ageLimit这个时间戳值小的元素并包含ageLimit的元素也删除,即需要找到>ageLimit的值:
vt.erase(vt.begin(), upper_bound(vt.begin(), vt.end(), ageLimit));
3 标准关联容器使用成员函数
之前的建议适合标准序列容器(vector、string、deque和list)。对于标准关联容器只需要选择同名的成员函数即可。只有binary_search例外,关联容器没有与之对应的成员函数。要在set或map中查找一个值是否存在,使用count习惯用法:
set<Widget> s;
...
Widget w;
...
if (s.count(w)) {
// 存在与w等价的值
...
} else {
...
}
对于multiset或multimap中测试一个值是否存在,则一般情况下使用find,因为find只要找到一个期望值就返回。
4 总结
表格如下:
想知道什么 | 使用算法 | 使用成员函数 | ||
未排序区间 | 排序区间 | set/map | multiset/multimap | |
特定的值是否存在 | find | binary_search | count | find |
特定的值存在哪里 | find | equal_range | find | find |
第一个不超过特定值的对象在哪里 | find_if | lower_bound | lower_bound | lower_bound |
第一个在特定值之后的对象在哪里 | find_if | upper_bound | upper_bound | upper_bound |
具有特定值的对象有多少个 | count | equal_range (distance) |
count | count |
具有特定值的对象都在哪里 | find (反复调用) |
equal_range | equal_range | euqal_range |
排序一栏中,equal_range出现的次数很多,因为在查找过程中使用等价性测试很重要。如果使用lower_bound或upper_bound,会很容易回退到相等性测试,需要额外注意。