[7 使用STL] 45. 正确区分count、find、binary_search、lower_bound、upper_bound和equal_range

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,会很容易回退到相等性测试,需要额外注意。

Guess you like

Origin blog.csdn.net/u012906122/article/details/120091370