1. 程式人生 > >C++ 11 特性:關聯容器map、set的使用

C++ 11 特性:關聯容器map、set的使用

參考文獻《C++ Primer》

一、關聯容器概述

1.1 關聯容器的概念

關聯容器支援高效的查詢與訪問,主要的關聯容器為mapset這兩個。其中map主要提供的是鍵-值的操作,比如字典;set主要提供的是集合的操作,比如去重。標準庫總計提供了8個關聯容器。

按關鍵字有序儲存元素

名稱 說明
map 關聯陣列,儲存<關鍵字,鍵>對
set 只儲存關鍵字的容器
multimap 關鍵字可以重複出現的map
multiset 關鍵字可以重複出現的set

無序集合

名稱 說明
unordered_map 使用雜湊函式組織的map
unordered_set 使用雜湊函式組織的set
unordered_multimap 使用雜湊函式組織的map,關鍵字可以重複出現
unordered_multiset 使用雜湊函式組織的set,關鍵字可以重複出現

8中關聯容器中,mapmultimap定義在標頭檔案<map> 中;setmultiset定義在標頭檔案<set> 中;無序容器則分別定義在標頭檔案<unordered_map><unordered_set>

中。

1.2 pair型別

在介紹關聯容器的操作前,需要掌握pair型別的操作,它定義在標頭檔案<utility>中。
pair通常用來生成一個特定型別的模板,儲存兩個資料成員。例如

pair <string, string> name;         //儲存兩個string
pair <string, size_t> word_count;   //儲存一個string和一個size_t
pair<string, vector<int>> line;     //儲存string和vector<int>

1.3 pair型別的操作


名稱 說明
pair < T1 , T2 > p p中第一個成員的型別為T1,第二個成員的型別為T2
pair < T1 , T2 > p(v1,v2) p中第一個成員初始化為v1,第二個成員初始化為v2
pair < T1 , T2 > p = {v1,v2} 等價p(v1,v2)
make_pair(v1,v2) 返回一個用v1,v2初始化的pair型別
p.first 返回p的第一個資料成員
p.second 返回p的第二個資料成員
p1 == p2 當第一個與第二個成員分別相等時,兩個pair相等

二、關聯容器操作

2.1 關聯容器的定義

關聯容器set與map的定義如下

map<string, size_t> word_count; //空容器
//每個<鍵,值>對在一個花括號中
map<string, string> name = { {"Joce","James"},{"Aust","Jane"}};
set<string> exclude = { "the","a","but","or" };


2.2 關聯容器的基本操作

2.2.1 關聯容器的相關型別

除了容器的基本型別外,關聯容器還具備如下型別。

名稱 說明
key_type 此容器型別的關鍵字型別
mapped_type 僅適用於map,關鍵字對應的值的型別
value_type 對於set與key_type相同

對於map為pair<const key_type,mapped_type> 型別

2.2.1 關聯容器與迭代器

map


map<string, string> word_count{ {"1.1","1.2"},{"2.1","2.2"} };
//迭代器指向map的首元素,這裡的map_iter指標指向的是pair<const string,string>型別
auto map_iter = word_count.begin(); 
//首元素的第一個成員,執行結果為1.1    
cout << map_iter->first << endl;
//第二個元素的第二個成員,執行結果為2.2      
cout << (++map_iter)->second << endl;
//錯誤,因為是pair<const string,string>型別,所以無法通過指標修改關鍵字
map_iter->first = "修改1.1";      
//正確,但是可以通過指標修改值。
map_iter->second = "修改2.2";     
//執行結果為:
//2.1 
//修改2.2 
cout << map_iter->first << endl;
cout << map_iter->second << endl;

mapvalue_typepair<const T1,T2> 型別的,我們可以修改pair的值,但是不能修改其關鍵字。

set

//定義一個集合
set<int> s = { 1,2,3,4,5 }; 
//迭代器set_iter指向集合首元素            
set<int>::iterator set_iter = s.begin();
while (set_iter != s.end())
{
    //正確,set_iter只讀
    cout << *set_iter << ends;          
    //錯誤,不能通過指標修改關鍵字
    (*set_iter) += 1;

    set_iter++;
}

雖然set中同時定義了iteratorconst_iterator,但它們都是隻讀的。

2.2.2 關聯容器的插入操作

map
首先我們要明確,插入map中的應該是一個鍵值對,也就是一個初始化了的pair型別的變數。下面給出了4中方法可以初始化pair型別,並插入到map中去。

map<string, string> word_count{{ "1.1","1.2" },{ "2.1","2.2" }};
//使用花括號初始化pair,然後插入到word_count中。
word_count.insert({ "3.1","3.2" });
//使用make_pair返回一個初始化了的pair。
word_count.insert(make_pair("4.1", "4.2"));
//直接定義一個初始化了的pair。
word_count.insert(pair<string, string>("5.1", "5.2"));
//直接定義一個map::value_type的pair型別。
word_count.insert(map<string, string>::value_type("6.1", "6.2"));

for (auto i : word_count)
    cout << i.first << ends << i.second << endl;
//執行結果:
//  1.1 1.2
//  2.1 2.2
//  3.1 3.2
//  4.1 4.2
//  5.1 5.2
//  6.1 6.2

set

//定義一個vector容器,包含重複資料。
vector<int> ivec = { 1,2,3,4,4,5,5 };
//遍歷結果1 2 3 4 4 5 5
for (auto i : ivec)
    cout << i << ends;
cout << endl;
//定義一個set關聯容器
set<int> set_ivec;
//使用insert插入操作,使用初始化列表
//set的特性去重。
set_ivec.insert({6,6,7,8});
//遍歷結果6 7 8
for (auto i : set_ivec)
    cout << i << ends;
cout << endl;
//使用insert插入操作的另一個版本,使用一對迭代器插入
//set的特性排序。
set_ivec.insert(ivec.begin(), ivec.end());
//遍歷結果1 2 3 4 5 6 7 8
for (auto i : set_ivec)
    cout << i << ends;
cout << endl;

insert的返回的值依賴於容器的型別和引數。對於map和set,新增單一元素的時候,返回的結果是一個pair,其第一個資料成員是一個迭代器,根據關鍵字,指向一個鍵值對;第二個資料成員是一個bool值,如果新增成功則為true,如果該關鍵字已經在容器中了則返回false

經典單詞計數程式

//統計每個單詞在輸入中出現的次數。
map<string, size_t>  word_count;
string word;
while (cin >> word)
{
    //若word已經在word_count中則insert什麼也不做
    auto ret = word_count.insert({ word,1 });
    if (!ret.second)
        ++ret.first->second;
        //上句等價於++((ret.first)->second)
}
//ret是一個pair
//ret.first是pair的第一個資料成員,是一個map的迭代器,根據關     鍵字指向鍵值對。
//ret.first->second是指向的那個鍵值對的第二個資料成員,在程式中是size_t型別,表示計數器。

multiset與multimap

對於允許重複關鍵字的容器,插入操作如下。

multimap<string,string> authors;
authors.insert({"John","Sot-weed"});
authors.insert({"John","Lost"});

2.2.3 關聯容器的刪除操作

對於刪除操作,關聯容器定義了三個版本的erase。

名稱 說明
c.erase(k) 從c中刪除關鍵字為k的元素,返回一個size_type值,指明刪除元素的數量
c.erase(p) 從c中刪除迭代器p指定的元素,返回一個指向p之後元素的迭代器,若p指向了c中的尾元素則返回c.end()
c.erase(b,e) 刪除迭代器從b到e範圍內的元素,返回e

2.2.4 map的下標操作

mapunordered_map提供了下標運算以及一個隊形的at函式,其他的關聯容器沒有提供。

map<string, size_t>  word_count;
//使用下標運算子
//如果該關鍵字已經在容器中,則獲取關鍵字對應的值
//如果該關鍵字不在容器中,則將一個新的鍵值對插入到容器,並將值初始化
word_count["Anna"] = 1;
//這裡提取出新插入的元素,並將值賦值為1
名稱 說明
c[k] 返回關鍵字為k的值,如果k不在c中,則新增一個關鍵字為k的鍵值對,並對其值進行初始化
c.at(k) 訪問關鍵字為k的元素,如果k不在c中,則丟擲異常

2.2.5 關聯容器的訪問操作

名稱 說明
c.find(k) 返回一個迭代器,指向第一個關鍵字為k的元素,若k不在容器中,則返回c.end()
c.count(k) 返回關鍵字為k的元素的數量,對於無重複關鍵字的容器,返回值永遠為0或1
c.lower_bound(k) 返回一個迭代器,指向第一個關鍵字不小於k的元素
c.upper_bound(k) 返回一個迭代器,指向第一個關鍵字大於k的元素
c.equal_range(k) 返回一個迭代器pair,表示關鍵字等於k的元素的範圍,如果k不在c中,pair的兩個成員都等於c.end()

在multimap或multiset中查詢元素

如果一個multimap或multiset中有多個元素具有相同關鍵字,這些元素會在容器中相鄰儲存。解決這種情況下的查詢問題可以有三種方法。

  • 1. 常用的方法是使用count方法,記錄下元素的數量,使用find找到首個值,然後使用迭代器遞加遍歷。
  • 2. 可以使用lower_boundupper_bound 兩種方法,完成相同的工作。
  • 3. 使用equal_range()函式,此函式接受一個關鍵字,返回一個迭代器pair,若關鍵字存在,則pair的第一個資料成員指向第一個與關鍵字匹配的元素,第二個資料成員指向最後一個和關鍵字匹配的元素之後的位置

對於無序容器如果有機會在做補充,如有不對的地方歡迎大家指正交流。