青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

網絡服務器軟件開發/中間件開發,關注ACE/ICE/boost

C++博客 首頁 新隨筆 聯系 聚合 管理
  152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks

條款45:注意count、find、binary_search、lower_bound、upper_bound和equal_range的區別

你要尋找什么,而且你有一個容器或者你有一個由迭代器劃分出來的區間——你要找的東西就在里面。你要怎么完成搜索呢?你箭袋中的箭有這些:count、count_if、find、find_if、binary_search、lower_bound、upper_bound和equal_range。面對著它們,你要怎么做出選擇?

簡單。你尋找的是能又快又簡單的東西。越快越簡單的越好。

暫時,我假設你有一對指定了搜索區間的迭代器。然后,我會考慮到你有的是一個容器而不是一個區間的情況。

要選擇搜索策略,必須依賴于你的迭代器是否定義了一個有序區間。如果是,你就可以通過binary_search、lower_bound、upper_bound和equal_range來加速(通常是對數時間——參見條款34)搜索。如果迭代器并沒有劃分一個有序區間,你就只能用線性時間的算法count、count_if、find和find_if。在下文中,我會忽略掉count和find是否有_if的不同,就像我會忽略掉binary_search、lower_bound、upper_bound和equal_range是否帶有判斷式的不同。你是依賴默認的搜索謂詞還是指定一個自己的,對選擇搜索算法的考慮是一樣的。

如果你有一個無序區間,你的選擇是count或著find。它們分別可以回答略微不同的問題,所以值得仔細去區分它們。count回答的問題是:“是否存在這個值,如果有,那么存在幾份拷貝?”而find回答的問題是:“是否存在,如果有,那么它在哪兒?”

假設你想知道的東西是,是否有一個特定的Widget值w在list中。如果用count,代碼看起來像這樣:

list<Widget> lw;			// Widget的list
Widget w;				// 特定的Widget值
...
if (count(lw.begin(), lw.end(), w)) {
...			// w在lw中
} else {
...			// 不在
}

這里示范了一種慣用法:把count用來作為是否存在的檢查。count返回零或者一個正數,所以我們把非零轉化為true而把零轉化為false。如果這樣能使我們要做的更加顯而易見:

if (count(lw.begin(), lw.end(), w) != 0) ...

而且有些程序員這樣寫,但是使用隱式轉換則更常見,就像最初的例子。

和最初的代碼比較,使用find略微更難懂些,因為你必須檢查find的返回值和list的end迭代器是否相等:

if (find(lw.begin(), lw.end(), w) != lw.end()) {
...				// 找到了
} else {
...				// 沒找到
}

如果是為了檢查是否存在,count這個慣用法編碼起來比較簡單。但是,當搜索成功時,它的效率比較低,因為當找到匹配的值后find就停止了,而count必須繼續搜索,直到區間的結尾以尋找其他匹配的值。對大多數程序員來說,find在效率上的優勢足以證明略微增加復雜度是合適的。

通常,只知道區間內是否有某個值是不夠的。取而代之的是,你想獲得區間中的第一個等于該值的對象。比如,你可能想打印出這個對象,你可能想在它前面插入什么,或者你可能想要刪除它(但當迭代時刪除的引導參見條款9)。當你需要知道的不止是某個值是否存在,而且要知道哪個對象(或哪些對象)擁有該值,你就得用find:

list<Widget>::iterator i = find(lw.begin(), lw.end(), w);
if (i != lw.end()) {
...				// 找到了,i指向第一個
} else {
...				// 沒有找到
}

對于有序區間,你有其他的選擇,而且你應該明確的使用它們。count和find是線性時間的,但有序區間的搜索算法(binary_search、lower_bound、upper_bound和equal_range)是對數時間的。

從無序區間遷移到有序區間導致了另一個遷移:從使用相等來判斷兩個值是否相同到使用等價來判斷。條款19由一個詳細地講述了相等和等價的區別,所以我在這里不會重復。取而代之的是,我會簡單地說明count和find算法都用相等來搜索,而binary_search、lower_bound、upper_bound和equal_range則用等價。

要測試在有序區間中是否存在一個值,使用binary_search。不像標準C庫中的(因此也是標準C++庫中的)bsearch,binary_search只返回一個bool:這個值是否找到了。binary_search回答這個問題:“它在嗎?”它的回答只能是是或者否。如果你需要比這樣更多的信息,你需要一個不同的算法。

這里有一個binary_search應用于有序vector的例子(你可以從條款23中知道有序vector的優點):

vector<Widget> vw;			// 建立vector,放入
...				// 數據,
sort(vw.begin(), vw.end());		// 把數據排序
Widget w;				// 要找的值
...
if (binary_search(vw.begin(), vw.end(), w)) {
...			// w在vw中
} else {
...			// 不在
}

如果你有一個有序區間而且你的問題是:“它在嗎,如果是,那么在哪兒?”你就需要equal_range,但你可能想要用lower_bound。我會很快討論equal_range,但首先,讓我們看看怎么用lower_bound來在區間中定位某個值。

當你用lower_bound來尋找一個值的時候,它返回一個迭代器,這個迭代器指向這個值的第一個拷貝(如果找到的話)或者到可以插入這個值的位置(如果沒找到)。因此lower_bound回答這個問題:“它在嗎?如果是,第一個拷貝在哪里?如果不是,它將在哪里?”和find一樣,你必須測試lower_bound的結果,來看看它是否指向你要尋找的值。但又不像find,你不能只是檢測lower_bound的返回值是否等于end迭代器。取而代之的是,你必須檢測lower_bound所標示出的對象是不是你需要的值。

很多程序員這么用lower_bound:

vector<Widget>::iterator i = lower_bound(vw.begin(), vw.end(), w);
if (i != vw.end() && *i == w) {	// 保證i指向一個對象;
// 也就保證了這個對象有正確的值。
// 這是個bug!
...			// 找到這個值,i指向
// 第一個等于該值的對象
} else {
...			// 沒找到
}

大部分情況下這是行得通的,但不是真的完全正確。再看一遍檢測需要的值是否找到的代碼:

if (i != vw.end() && *i == w) ...

這是一個相等的測試,但lower_bound搜索用的是等價。大部分情況下,等價測試和相等測試產生的結果相同,但就像條款19論證的,相等和等價的結果不同的情況并不難見到。在這種情況下,上面的代碼就是錯的。

要完全完成,你就必須檢測lower_bound返回的迭代器指向的對象的值是否和你要尋找的值等價。你可以手動完成(條款19演示了你該怎么做,當它值得一做時條款24提供了一個例子),但可以更狡猾地完成,因為你必須確認使用了和lower_bound使用的相同的比較函數。一般而言,那可以是一個任意的函數(或函數對象)。如果你傳遞一個比較函數給lower_bound,你必須確認和你的手寫的等價檢測代碼使用了相同的比較函數。這意味著如果你改變了你傳遞給lower_bound的比較函數,你也得對你的等價檢測部分作出修改。保持比較函數同步不是火箭發射,但卻是另一個要記住的東西,而且我想你已經有很多需要你記的東西了。

這兒有一個簡單的方法:使用equal_range。equal_range返回一對迭代器,第一個等于lower_bound返回的迭代器,第二個等于upper_bound返回的(也就是,等價于要搜索值區間的末迭代器的下一個)。因此,equal_range,返回了一對劃分出了和你要搜索的值等價的區間的迭代器。一個名字很好的算法,不是嗎?(當然,也許叫equivalent_range會更好,但叫equal_range也非常好。)

對于equal_range的返回值,有兩個重要的地方。第一,如果這兩個迭代器相同,就意味著對象的區間是空的;這個只沒有找到。這個結果是用equal_range來回答“它在嗎?”這個問題的答案。你可以這么用:

vector<Widget> vw;
...
sort(vw.begin(), vw.end());
typedef vector<Widget>::iterator VWIter;	// 方便的typedef
typedef pair<VWIter, VWIter> VWIterPair;
VWIterPair p = equal_range(vw.begin(), vw.end(), w);
if (p.first != p.second) {			// 如果equal_range不返回
// 空的區間...
...				// 說明找到了,p.first指向
// 第一個而p.second
// 指向最后一個的下一個
} else {
...				// 沒找到,p.first和
// p.second都指向搜索值
}					// 的插入位置

這段代碼只用等價,所以總是正確的。

第二個要注意的是equal_range返回的東西是兩個迭代器,對它們作distance就等于區間中對象的數目,也就是,等價于要尋找的值的對象。結果,equal_range不光完成了搜索有序區間的任務,而且完成了計數。比如說,要在vw中找到等價于w的Widget,然后打印出來有多少這樣的Widget存在,你可以這么做:

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

到目前為止,我們所討論的都是假設我們要在一個區間內搜索一個值,但是有時候我們更感興趣于在區間中尋找一個位置。比如,假設我們有一個Timestamp類和一個Timestamp的vector,它按照老的timestamp放在前面的方法排序:

class Timestamp { ... };
bool operator<(const Timestamp& lhs,		// 返回在時間上lhs
const Timestamp& rhs);		// 是否在rhs前面
vector<Timestamp> vt;			// 建立vector,填充數據,
...					// 排序,使老的時間
sort(vt.begin(), vt.end());			// 在新的前面

現在假設我們有一個特殊的timestamp——ageLimit,而且我們從vt中刪除所有比ageLimit老的timestamp。在這種情況下,我們不需要在vt中搜索和ageLimit等價的Timestamp,因為可能不存在任何等價于這個精確值的元素。 取而代之的是,我們需要在vt中找到一個位置:第一個不比ageLimit更老的元素。這是再簡單不過的了,因為lower_bound會給我們答案的:

Timestamp ageLimit;
...
vt.erase(vt.begin(), lower_bound(vt.begin(),	// 從vt中排除所有
vt.end(),				// 排在ageLimit的值
ageLimit));			// 前面的對象

如果我們的需求稍微改變了一點,我們要排除所有至少和ageLimit一樣老的timestamp,也就是我們需要找到第一個比ageLimit年輕的timestamp的位置。這是一個為upper_bound特制的任務:

vt.erase(vt.begin(), upper_bound(vt.begin(),	// 從vt中除去所有
vt.end(),				// 排在ageLimit的值前面
ageLimit));			// 或者等價的對象

如果你要把東西插入一個有序區間,而且對象的插入位置是在有序的等價關系下它應該在的地方時,upper_bound也很有用。比如,你可能有一個有序的Person對象的list,對象按照name排序:

class Person {
public:
...
const string& name() const;
...
};
struct PersonNameLess:
public binary_function<Person, Person, bool> {	// 參見條款40
bool operator()(const Person& lhs, const Person& rhs) const
{
return lhs.name() < rhs.name();
}
};
list<Person> lp;
...
lp.sort(PersonNameLess());			// 使用PersonNameLess排序lp

要保持list仍然是我們希望的順序(按照name,插入后等價的名字仍然按順序排列),我們可以用upper_bound來指定插入位置:

Person newPerson;
...
lp.insert(upper_bound(lp.begin(),		// 在lp中排在newPerson
lp.end(),				// 之前或者等價
newPerson,			// 的最后一個
PersonNameLess()),			// 對象后面
newPerson);			// 插入newPerson

這工作的很好而且很方便,但很重要的是不要被誤導——錯誤地認為upper_bound的這種用法讓我們魔術般地在一個list里在對數時間內找到了插入位置。我們并沒有——條款34解釋了因為我們用了list,查找花費線性時間,但是它只用了對數次的比較。

一直到這里,我都只考慮我們有一對定義了搜索區間的迭代器的情況。通常我們有一個容器,而不是一個區間。在這種情況下,我們必須區別序列和關聯容器。對于標準的序列容器(vector、string、deque和list),你應該遵循我在本條款提出的建議,使用容器的begin和end迭代器來劃分出區間。

這種情況對標準關聯容器(set、multiset、map和multimap)來說是不同的,因為它們提供了搜索的成員函數,它們往往是比用STL算法更好的選擇。條款44詳細說明了為什么它們是更好的選擇,簡要地說,是因為它們更快行為更自然。幸運的是,成員函數通常和相應的算法有同樣的名字,所以前面的討論推薦你使用的算法count、find、equal_range、lower_bound或upper_bound,在搜索關聯容器時你都可以簡單的用同名的成員函數來代替。

調用binary_search的策略不同,因為這個算法沒有提供對應的成員函數。要測試在set或map中是否存在某個值,使用count的慣用方法來對成員進行檢測:

set<Widget> s;		// 建立set,放入數據
...
Widget w;			// w仍然是保存要搜索的值
...
if (s.count(w)) {
...		// 存在和w等價的值
} else {
...		// 不存在這樣的值
}

要測試某個值在multiset或multimap中是否存在,find往往比count好,因為一旦找到等于期望值的單個對象,find就可以停下了,而count,在最遭的情況下,必須檢測容器里的每一個對象。(對于set和map,這不是問題,因為set不允許重復的值,而map不允許重復的鍵。)

但是,count給關聯容器計數是可靠的。特別,它比調用equal_range然后應用distance到結果迭代器更好。首先,它更清晰:count 意味著“計數”。第二,它更簡單;不用建立一對迭代器然后把它的組成(譯注:就是first和second)傳給distance。第三,它可能更快一點。

要給出所有我們在本條款中所考慮到的,我們的從哪兒著手?下面的表格道出了一切。

你想知道的 使用的算法 使用的成員函數
在無序區間 在有序區間 在set或map上 在multiset或multimap上
期望值是否存在? find binary_search count find
期望值是否存在?如果有,第一個等于這個值的對象在哪里? find equal_range find find或lower_bound(參見下面)
第一個不在期望值之前的對象在哪里? 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 equal_range

上表總結了要怎么操作有序區間,equal_range的出現頻率可能令人吃驚。當搜索時,這個頻率因為等價檢測的重要性而上升了。對于lower_bound和upper_bound,它很容易在相等檢測中退卻,但對于equal_range,只檢測等價是很自然的。在第二行有序區間,equal_range打敗了find還因為一個理由:equal_range花費對數時間,而find花費線性時間。

對于multiset和multimap,當你在搜索第一個等于特定值的對象的那一行,這個表列出了find和lower_bound兩個算法作為候選。 已對于這個任務find是通常的選擇,而且你可能已經注意到在set和map那一列里,這項只有find。但是對于multi容器,如果不只有一個值存在,find并不保證能識別出容器里的等于給定值的第一個元素;它只識別這些元素中的一個。如果你真的需要找到等于給定值的第一個元素,你應該使用lower_bound,而且你必須手動的對第二部分做等價檢測,條款19的內容可以幫你確認你已經找到了你要找的值。(你可以用equal_range來避免作手動等價檢測,但是調用equal_range的花費比調用lower_bound多得多。)

在count、find、binary_search、lower_bound、upper_bound和equal_range中做出選擇很簡單。當你調用時,選擇算法還是成員函數可以給你需要的行為和性能,而且是最少的工作。按照這個建議做(或參考那個表格),你就不會再有困惑。

posted on 2007-04-19 15:47 true 閱讀(524) 評論(0)  編輯 收藏 引用 所屬分類: STL

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品久久久久久久久免费 | 这里只有精品视频| 一色屋精品视频在线观看网站| 亚洲美女精品成人在线视频| 在线日韩成人| 久久xxxx| 欧美在线一级视频| 国产精品黄视频| 亚洲精品久久久久久下一站| 亚洲成人影音| 久久久久一区二区| 久久精品一区中文字幕| 国产精品日本精品| 99热这里只有精品8| 99国产精品私拍| 欧美福利在线观看| 一区一区视频| 久久九九99| 老司机精品视频一区二区三区| 国产一区二区在线观看免费| 亚洲一区欧美一区| 午夜在线a亚洲v天堂网2018| 国产精品乱人伦一区二区 | 亚洲天堂成人| 一区二区三区在线免费播放| 香蕉av777xxx色综合一区| 久久国产欧美日韩精品| 欧美日韩成人网| 亚洲理伦在线| 亚洲综合欧美| 国产精品自拍一区| 午夜视频在线观看一区二区| 久久久久88色偷偷免费| 亚洲电影网站| 欧美激情精品久久久| 亚洲精品精选| 欧美一级片一区| 一区一区视频| 欧美—级a级欧美特级ar全黄| 一本色道88久久加勒比精品| 亚洲在线视频观看| 国产亚洲精品久久久久久| 久久久噜噜噜久久| 亚洲国产日本| 亚洲欧美一区二区精品久久久| 欧美激情四色 | 久久久久久久综合狠狠综合| 欧美国产日韩一区二区三区| 99日韩精品| 国产三区二区一区久久| 久久一二三四| 9l视频自拍蝌蚪9l视频成人| 久久精品国产一区二区电影 | 欧美一区二区三区免费视| 国产亚洲精品7777| 欧美二区乱c少妇| 亚洲欧美国产毛片在线| 欧美黄色大片网站| 午夜精品福利在线| 在线电影一区| 国产精品豆花视频| 另类亚洲自拍| 亚洲一区二区毛片| 欧美国产精品专区| 欧美一区二区三区四区在线观看地址 | 这里只有精品丝袜| 久久视频在线视频| 亚洲私拍自拍| 最近中文字幕mv在线一区二区三区四区 | 日韩亚洲欧美综合| 韩日精品在线| 国产精品美女久久久免费| 欧美va亚洲va日韩∨a综合色| 亚洲视频第一页| 亚洲国产高潮在线观看| 久久久欧美一区二区| 亚洲一区二区视频在线| 在线欧美一区| 欧美+日本+国产+在线a∨观看| 欧美特黄一级| 久久综合五月天婷婷伊人| 日韩亚洲综合在线| 欧美xart系列高清| 欧美一区二区视频在线观看2020| 国产亚洲精品久久久久婷婷瑜伽 | 久久精品视频在线| 一本一本久久| 亚洲精品久久久久久下一站| 免费亚洲一区二区| 久久久久国产精品麻豆ai换脸 | 亚洲无限av看| 亚洲理论在线观看| 亚洲电影成人| 亚洲承认在线| 1000部精品久久久久久久久| 激情欧美一区二区三区| 国产亚洲成精品久久| 国产精品美女久久| 国产精品毛片在线看| 欧美日韩一级黄| 欧美日韩国产一区二区三区| 欧美成人乱码一区二区三区| 久久综合一区二区三区| 久久久久久夜| 久久免费高清| 美女黄网久久| 欧美www视频在线观看| 欧美ed2k| 欧美激情亚洲综合一区| 欧美成人在线影院| 欧美激情视频在线播放 | 欧美午夜精品久久久久久人妖| 欧美高清在线一区二区| 欧美成人精品1314www| 欧美国产三区| 欧美午夜无遮挡| 国产精品国产三级国产aⅴ浪潮 | 国产小视频国产精品| 国产日产亚洲精品系列| 国产专区欧美专区| 久久久蜜桃精品| 久久一区二区精品| 欧美二区在线看| 欧美小视频在线观看| 国产精品免费看片| 韩国在线一区| 亚洲国产精品热久久| 99在线精品视频| 亚洲一区图片| 久久久久久97三级| 亚洲丰满少妇videoshd| aa成人免费视频| 亚洲欧美国产毛片在线| 久久人人爽人人爽爽久久| 免费成人高清视频| 国产精品h在线观看| 国产亚洲网站| 91久久国产综合久久| 亚洲综合电影| 免费观看成人鲁鲁鲁鲁鲁视频| 91久久久久久| 亚洲中无吗在线| 欧美不卡一区| 国产精品一级二级三级| 亚洲国产裸拍裸体视频在线观看乱了中文| 99re66热这里只有精品3直播| 性18欧美另类| 麻豆国产精品va在线观看不卡| 亚洲日本黄色| 久久精品日产第一区二区| 欧美极品一区| 黑人巨大精品欧美黑白配亚洲| 夜夜嗨av一区二区三区| 久久亚洲视频| 亚洲午夜影视影院在线观看| 理论片一区二区在线| 国产欧美日韩麻豆91| 一区二区电影免费在线观看| 久久色在线播放| 一区二区三区色| 欧美mv日韩mv亚洲| 黄色亚洲在线| 午夜精品久久久久久久久久久久| 亚洲电影中文字幕| 久久激情五月丁香伊人| 国产精品国产精品| 99re66热这里只有精品4| 午夜亚洲一区| 99热精品在线观看| 欧美成人免费在线| 狠狠色丁香久久婷婷综合_中| 久久频这里精品99香蕉| 一道本一区二区| 亚洲欧美国产精品桃花| 欧美国产一区二区| 在线成人小视频| 久久精品国产精品亚洲综合| 99精品国产一区二区青青牛奶| 麻豆精品精华液| 伊人精品视频| 久久这里只有| 久久激情五月丁香伊人| 国产一区二区三区久久久久久久久| 亚洲欧美一级二级三级| 一区二区三区高清不卡| 欧美日韩一本到| 日韩视频免费大全中文字幕| 亚洲高清在线播放| 美女在线一区二区| 亚洲国产欧美一区| 欧美成人亚洲| 欧美1区2区3区| 亚洲青涩在线| 亚洲韩国青草视频| 欧美屁股在线| 亚洲在线1234| 亚洲午夜三级在线| 国产精品一区二区在线观看不卡| 亚洲欧美另类久久久精品2019| 亚洲一区二区黄色|