C++ primer 第11章 关联容器
文章目錄
- 使用關聯容器
- map示例
- 關聯容器概述
- 定義關聯容器
- 關聯容器值初始化
- multimap和multiset
- 關鍵字類型的要求
- pair類型
- pair上的操作
- 關聯容器操作
- 關聯容器額外的類型別名
- 關聯容器迭代器
- map迭代器
- set迭代器
- 關聯容器和算法
- 添加元素
- 向map添加元素
- 檢測insert的返回值
- 使用insert代替下標操作寫單詞計數程序
- 刪除元素
- map的下標操作
- 訪問元素
- multimap查找元素代碼示例一:count和find
- multimap查找元素代碼示例二:lower_bound和upper_bound
- multimap查找元素代碼示例三:equal_range函數
- 無序容器
- 無序容器管理操作
關聯容器和順序容器有著根本的不同:關聯容器中的元素是按關鍵字來保存和訪問的,順序容器中的元素是按它們在容器中的位置來順序保存和訪問的。
兩個主要的管理容器類型是map和set。map中的元素是key-value對,set中每個元素只包含一個關鍵字,set支持高效的關鍵字查詢操作——檢查一個給定關鍵字是否在set中。
使用關聯容器
map示例
map<string, size_t>word_count;string word;while (cin>>word) {++word_count[word];}for (const auto &wc:word_count) {cout << wc.first << " 出現的次數:" << wc.second << endl;}輸入:
hello hi haha hello haha ha ^Z輸出:
ha 出現的次數:1 haha 出現的次數:2 hello 出現的次數:2 hi 出現的次數:1關聯容器概述
關聯容器不支持順序容器的位置相關的操作,例如push_front或push_back。關聯容器也不支持構造函數或插入操作這些接受一個元素值和一個數量值的操作。
定義關聯容器
關聯容器值初始化
set<string>st={"set","the","hello"}; map<string,string>mp={{"zhang","san"},{"wang","wu"},{"li","si"}};multimap和multiset
multimap和multiset允許關鍵字重復。
關鍵字類型的要求
關聯容器對其關鍵字類型有一些限制。對于有序容器——map、multimap、set以及multiset,關鍵字類型必須定義元素比較的方法。
例如,我們不能直接定義Sales_data的multiset,因為Sales_data沒有<運算符,但是可以用compareIsbn函數來定義一個multiset,次函數在Sales_data對象的ISBN成員上定義了一個嚴格弱序。
此處我們使用decltype來指出自定義操作的類型。當用decltype來獲得一個函數指針類型時,必須加上一個*來指出我們要使用一個給定函數類型的指針。用compareIsbn來初始化bookstore對象,表示當我們向bookstore添加元素時,通過調用compareIsbn來為這些元素排序。可以用compareIsbn代替&compareIsbn作為構造函數的參數,因為當我們使用一個函數的名字時,在需要的情況下它會自動轉化為一個指針,當然,使用&compareIsbn的效果也是一樣的。
pair類型
標準庫類型pair定義在頭文件utility中。
一個pair保存兩個數據成員,類似容器,pair是一個用來生成特定類型的模板。
例如: pair<string,int>p;
與其他標準庫類型不同,pair的數據成員是public的,兩個成員名分別為first和second。
pair上的操作
關聯容器操作
關聯容器額外的類型別名
set<string>::value_type v1; v1是一個string set<string>::key_type v2; v2是一個string map<string,int>::key_type v3; v3是一個string map<string,int>::mapped_type v4; v4是一個int map<string,int>::value_type v5; v5是一個pair<const string ,int>v5是一個pair<const string ,int>,關鍵字是const,不能改變
關聯容器迭代器
map迭代器
當解引用一個關聯容器迭代器時,我們會得到一個類型為容器的value_type的值的引用。下述代碼不能執行語句
it->first=“newKey”,因為關鍵字是const!!
但可執行語句 it->second = 10;
輸出結果:
a 1 a 1 a 10 b 2set迭代器
與不能改變一個map元素的關鍵字一樣,set中的關鍵字也是const的,可以用一個set迭代器來讀取元素的值,但不能修改:
set<int>st{1,3,5,7,9,2,3,4,6,5};auto it = st.begin();while (it!=st.end()) {//*it = 100; 此句錯誤,關鍵字是const的,只能讀不能修改cout << *it << " ";it++;}輸出結果:
1 2 3 4 5 6 7 9關聯容器和算法
我們通常不對關聯容器使用泛型算法。關鍵字是const這一特性意味著不能將關聯容器傳遞給修改或重排容器元素的算法,因為這類算法需要向元素寫入值,而set類型中的元素是const的,map中的元素是pair,且其第一個成員是const的。
關聯容器可用于只讀取元素的算法,但是很多這類算法都要搜索序列。關聯容器定義了一個名為find的成員,它通過一個給定的關鍵字直接獲取元素,我們可以用泛型find算法來查找一個元素,但此算法會進行順序搜索。使用關聯容器定義的專用的find成員會比調用泛型find快得多。
在實際編程中,如果我們真要對一個關聯容器使用算法, 要么是將它當做一個源序列,要么當做一個目的位置。例如可以用泛型copy算法將元素從一個關聯容器拷貝到另一個序列。類似的,可以調用inserter將一個插入器綁定到一個關聯容器。通過使用inserter,我們可以將關聯容器當做一個目的位置來調用另一個算法。
添加元素
向map添加元素
向map用insert添加元素的四種方法:
map<string,int>wc; string words="hello"; wc.insert({words,1}); wc.insert(make_pair(words,1)); wc.insert(pair<string,int>(words,1)); wc.insert(map<string,int>::value_type(words,1));檢測insert的返回值
添加單一元素的insert和emplace版本返回一個pair,pair的first成員是一個迭代器,指向具有給定關鍵字的元素;second成員是一個bool值,指出元素是插入成功還是已經存在于容器中,如果插入成功,則返回true,否則返回false。
map<string,int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it1 = mp.insert({ "b",10 });//it1是一個paircout << it1.second<<endl;auto mp1 = it1.first; //mp1是一個迭代器cout << mp1->first << " "<<mp1->second << endl;auto it2 = mp.insert({ "a2",10 });cout << it2.second << endl;auto mp2 = it2.first;cout << mp2->first << " " << mp2->second << endl;輸出結果:
0 b 2 1 a2 10第一次插入失敗,insert返回的迭代器指向map已存在的元素。
第二次插入成功,insert返回的迭代器指向插入的元素。
而對于允許重復的multimap,insert操作返回一個指向新元素的迭代器,無需返回bool值,因為insert總是向這類容器中加入一個新元素。
使用insert代替下標操作寫單詞計數程序
map<string, int>mp;string str;while (cin>>str) {auto ret = mp.insert({ str,1 });if (!ret.second) {auto tmp = ret.first;(tmp->second)++;}}for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}輸入:
hello hi haha hahaha haha hi hello hello helll hehehe ^Z輸出:
haha 2 hahaha 1 hehehe 1 helll 1 hello 3 hi 2刪除元素
關聯容器定義了三個版本的erase,如圖所示:
與順序容器一樣,我們可以通過傳遞給erase一個迭代器或一個迭代器對來刪除一個元素或者一個元素范圍。
map的下標操作
map和unodered_map容器提供了下標運算符和一個對應的at函數,set類型不支持下標,因為set中沒有與關鍵字相關聯的“值”。我們不能對一個multimap或一個unodered_multimap進行下標操作,因為這些容器中可能有多個值與一個關鍵字相關聯。
map下標運算符接受一個索引(即,一個關鍵字),獲取與此關鍵字相關聯的值。但是,與其他下標運算符不同的是,如果關鍵字并不在map中,會為它創建一個元素并插入到map中,關聯值將進行值初始化。例如下述代碼,mp["d"];使得mp中增加了元素{ “d”,0 }。
map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}cout << endl;mp["d"];for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}輸出結果:
a 1 b 2 c 3a 1 b 2 c 3 d 0由于下標運算符可能插入一個新元素,我們只可以對非const的map使用下標操作。
與vector和string不同,map的下標運算符返回的類型(mapped_type)與解引用map迭代器得到的類型(value_type)不同。
訪問元素
multimap查找元素代碼示例一:count和find
給定一個作者到著作的映射,打印一個特定的作者的所有著作
string item("luxun"); auto entries = authors.count(item); auto iter = authors.find(item); while(entries){cout<<iter->second<<endl;++iter;--entries; }multimap查找元素代碼示例二:lower_bound和upper_bound
可以用lower_bound和upper_bound解決此問題。lower_bound返回的迭代器將指向第一個具有給定關鍵字的元素,而upper_bound返回的迭代器則指向最后一個匹配給定關鍵字的元素之后的位置。因此用相同的關鍵字調用lower_bound和upper_bound會得到一個迭代器范圍,表示所有具有該關鍵字的元素。如果關鍵字不存在,則lower_bound和upper_bound指向相同的位置,迭代器范圍即為空。
重寫代碼示例一的程序如下:
multimap查找元素代碼示例三:equal_range函數
equal_range:接受一個關鍵字,返回一個迭代器pair,第一個迭代器指向第一個與關鍵字匹配的元素,第二個迭代器指向最后一個匹配元素之后的位置,若未找到匹配元素,則兩個迭代器都指向關鍵字可以插入的位置,即upper_bound返回的迭代器的位置。
for(auto pos = authors.equal_range(item); pos.first!=pos.second;++pos.first){cout<<pos.first->second<<endl; }無序容器
unordered_map
unordered_set
unordered_multimap
unordered_multiset
無序容器在存儲上組織為一組桶,每個桶保存零個或多個元素。無序容器使用一個哈希函數將元素映射到桶。為了訪問一個元素,容器首先計算元素的哈希值,它指出應該搜索哪個桶。容器將具有一個特定哈希值的所有元素都保存在相同的桶中。如果容器允許重復關鍵字,所有具有相同關鍵字的元素也都會在同一個桶中。因此,無序容器的性能依賴于哈希函數的質量和桶的數量和大小。
無序容器管理操作
總結
以上是生活随笔為你收集整理的C++ primer 第11章 关联容器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 满帮怎么跑到美国上市 为什么满帮要在美国
- 下一篇: C++ primer 第9章 顺序容器