您的位置:首页 > 编程语言 > C语言/C++

C++ STL的查找算法

2015-09-08 15:48 405 查看
假设你有一个序列容器,或者有一对迭代器标识了一个区间,现在你希望在容器中查找一些信息,这样的查找工作如何进行呢?你的选择往往是:

count,count_if,find,find_if,binary_search,lower_bound,upper_bound,equal_range.该如何选择呢?

现在,我们假设你有了一对迭代器,他们指定了一个被选择的区间。

在选择具体的策略时,需要考虑由迭代器指定的区间是否是排序的,这是一个至关重要的决定条件。

未排序区间

如果迭代器并没有指定一个排序的区间,那么你的选择是count/count_if,find/find_if.这些算法提供线性时间的效率。

count

问题:

区间中是否有某个特定的值?如果有,有几个?

用法:

list<Widget> lw;
Widget w;
...
if(count(lw.begin(),lw.end(),w)!=0)//w在lw中
{
....
}
else//w不在lw中
{
.....
}


这段代码演示了一种常见的习惯用法:将count用在存在性测试。count返回0或者一个正整数

find

问题:

区间中是否有某个特定的值?如果有,在哪里?

用法:

if(find(lw.begin(),lw.end(),w)!=lw.end())//存在w
{
...
}
else//不存在w
{
...
}


从存在性测试的角度来看,count的习惯用法较为容易编码一些,但同时,他的效率要差一些。因为find一旦找到第一个匹配的结果后马上返回,而count必须到达区间的末尾,以便找到所有的匹配。

排序区间

对于已经排序的区间,我们将使用binary_search、lower_bound、upper_bound、equal_range.它们以对数时间运行。其实他们的背后是二分查找法。

binary_search

问题:

binary_search仅仅返回的是一个bool值:是否找到了特定的值

用法:

vector<Widget> vw;
...
sort(vw.begin(),vw.end());
Widget w;
...
if(binary_search(vw.begin(),vw.end(),w))
{
...//w在vw中
}
else
{
...//w不在vw中
}


lower_bound

问题:

这个值在区间中吗?如果在,那么第一个拷贝在哪里?如果不在,他该往哪里插入?

用法

我们仅将lower_bound用在下面的情景中

假设我们有一个Timestamp类和一个存放Timestamp的vector,并且这个vector已经排过序,其中老的时间排在前面。

class Timestamp{...};
bool operator<(const Timestamp& lhs,const Timestamp& rhs);//判断lhs是否在rhs之前
vector<Timestamp> vt;
...
sort(vt.begin(),vt.end());


现在假设有一个特殊的时间戳,ageLimit,我们希望删除所有在ageLimit之前的Timestamp对象。在这种情况下,我们并不想找到

该区间中与ageLimit等价的Timestamp类,因为该区间中可能根本没有与它等价的对象。我们其实想在vt中找到一个位置:第一个不比ageLimit老的位置。这是非常容易的,因为lower_bound给我们一个准确地答案:

Timestamp ageLimit;
...
vt.erase(vt.begin(),lower_bound(vt.begin(),vt.end(),ageLimit));//删除所有在ageLimit之前的对象


那么如果我们想删除那些至少和ageLimit一些老的对象呢?

这就是我们即将看到的upper_bound

upper_bound

问题:

这个值在区间中吗?如果在,那么最后一个拷贝的下一个位置在哪里?如果不在,他该往哪里插入?

用法

同样我们也只考虑以上给出的应用,

代码如下:

vt.erase(vt.begin(),upper_bound(vt.begin(),vt.end(),ageLimit));//从vt中删除所有在ageLimit之前或者与ageLimit等价的对象


我们看到lower_bound与upper_bound的用法与给出的问题,似乎有点不合,实际上,我在这里做了简化,只列出了使用以上两个函数的最常见和最应该使用之处,至于其它,建议参考《Effective STL》45条

equal_range

问题:

这个值在区间中吗?如果在,在哪里?

用法

vector<Widget> vw;
...
sort(vw.begin(),vw.end());
typedef vector<Widget>::iterator VWIter;
typedef pair<VWIter,VWIter> VWIterPair;
VWIterPair p=eauql_range(vw.begin(),vw.end(),w);
if(p.first!=p.second)//如果equal_range返回非空区间,
{                   //找到了特定值,p.first指向第一个与w等价的
...                 //对象,p.second指向最后一个与w等价的对象
}                   //的下一个位置
else
{
...                   //没有找到特定值,p.first与p.second都
}                     //指向w的插入位置


而且,你还可以打印出有多少个这样的对象。

VWIterPair p=eauql_range(vw.begin(),vw.end(),w);
cout<<"There are"<<distance(p.first,p.second)<<"elements in vw equivalent to w.";


注意:

以上我们的方法使用与序列容器如:vector,string,deque,list。

关联容器也有count.find,equal_range,lower_bound,upper_bound成员函数。凡是前面的讨论中建议选择以上算法的,在关联容器中只要使用同名的成员函数即可。只有binary_search例外,因为关联容器中没有这个成员函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: