假設你有一個序列容器,或者有一對迭代器標識了一個區間,現在你希望在容器中查找一些信息,這樣的查找工作如何進行呢?你的選擇往往是:
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例外,因為關聯容器中沒有這個成員函數。