日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++之关联容器

發(fā)布時間:2023/12/13 c/c++ 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++之关联容器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 概述及類型
  • map
  • set
  • pair類型
    • 概念
    • 初始化
      • 默認初始化
      • 提供初始化器
    • 允許的操作
      • 可以創(chuàng)建一個pair類的函數(shù)
      • 可以作為容器的類型
  • 關聯(lián)容器迭代器
    • 概念
    • map的迭代器
    • set的迭代器是const的
  • 初始化
    • map and set
    • multimap and multiset
  • 關聯(lián)容器的操作
    • 額外的類型別名
    • 關聯(lián)容器和算法
    • 刪除元素
    • 添加元素
      • insert操作
        • 通過insert操作向map添加元素
        • insert的返回值
        • 向兩種multi容器添加元素
    • map的下標操作
      • 下標操作的返回值
      • 通過下標([])對map的key與value進行操作:
    • 訪問元素
      • 概念
      • 利用find和count進行查找
      • lower_bound和upper_bound面向迭代器
      • equal_range函數(shù)
    • 下標和insert的區(qū)別、下標與find的區(qū)別
  • 有序容器
    • 關鍵字類型
    • 重載關鍵字類型代替<運算符
  • 無序容器
    • 關鍵字類型
    • 管理桶
  • 關鍵字相同的元素相鄰存儲


概述及類型

關聯(lián)容器和順序容器有著根本的不同:

  • 關聯(lián)容器中的元素是按關鍵字來保存和訪問的。
  • 順序容器中的元素是按它們在容器中的位置來順序保存和訪問的。
  • 順序容器的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組、鏈表。
  • 關聯(lián)容器的底層數(shù)據(jù)結(jié)構(gòu)是紅黑樹(set、multimap)、哈希表等。

關聯(lián)容器支持高效的關鍵字查找和訪問。兩個主要的關聯(lián)容器(associative-container)類型是map和set:

  • map中的元素是一些關鍵字-值(key-value)對。
  • set中每個元素只包含一個關鍵字;

標準庫提供8個關聯(lián)容器,這8個容器間的不同體現(xiàn)在三個維度上:每個容器

  • 或者是一個set,或者是一個map;
  • 或者要求不重復的關鍵字,或者允許重復關鍵字;
  • 按順序保存元素,或無序保存。
  • 特性由名字表示:

    • 允許重復關鍵字的容器的名字中都包含單詞multi;
    • 不保持關鍵字按順序存儲的容器的名字都以單詞unordered開頭。

    因此一個unordered_multi_set是一個允許重復關鍵字,元素無序保存的集合,而一個set則是一個要求不重復關鍵字,有序存儲的集合。無序容器使用哈希函數(shù)來組織元素。

    所在的頭文件:

    • 類型map和multimap定義在頭文件map中;
    • set和multiset定義在頭文件set中;
    • 無序容器則定義在頭文件unordered_map和unordered_set中。


    下表中的操作關聯(lián)容器和順序容器都支持:



    map

    為了高效實現(xiàn) “按值訪問元素” 這個操作而設計的。

    當從map中提取一個元素時,會得到一個pair類型的對象。

    簡單來說,pair是一個模板類型,保存兩個名為 firstsecond 的(公有)數(shù)據(jù)成員。



    set

    保存特定的值(如:滿足/不滿足,忽略/不忽略)集合。



    pair類型

    概念

    • pair是標準庫類型,定義在頭文件utility中。
    • pair的數(shù)據(jù)成員是public的。兩個成員分別命名為 first 和 second。可以用成員訪問符號(.)來訪問它們。
    • map的元素是pair,pair用first成員保存關鍵字,用second成員保存對應的值。

    初始化

    默認初始化

    一個pair保存兩個數(shù)據(jù)成員。創(chuàng)建一個pair時,我們必須提供兩個類型名,但是不要求一樣:

    pair的默認構(gòu)造函數(shù)對數(shù)據(jù)成員進行值初始化。因此:

    • anon是兩個空的string
    • line保存一個空的string一個空的vector
    • word_count保存一個空串和一個為0的size_t

    提供初始化器

    pair <string, string> anon = {"zhangsan", "nan"};

    允許的操作


    可以創(chuàng)建一個pair類的函數(shù)

    可以利用c++11標準下,允許對返回值進行列表初始化的特性,完成這一目的:

    pair<string, int> fun(vector<string> &vs){if(!vs.empty())return {vs.back(), vs.back().size()}; // 列表初始化//返回尾元素和尾元素的大小組成的pairelsereturn pair<string,int>(); // 隱式構(gòu)造返回值,返回一個空串和0構(gòu)成的pair }

    也可以用make_pair來生成pair對象,pair的兩個類型來自于make_pair的參數(shù):

    return make_pair(vs.back(), v.back().size());

    可以作為容器的類型


    push_back的參數(shù)也可以寫成列表初始化的形式或者使用make_pair:

    arr.push_back({s, i}); arr.push_back(make_pair(s, i));

    關聯(lián)容器迭代器

    概念

    當解引用一個關聯(lián)容器迭代器時,會得到一個類型為容器的value_type的值的引用。

    迭代器的類型:

    • map:pair<關鍵字類型,值類型>::iterator
    • set:set<元素值類型>::iterator

    對于自定義的比較類型的set或者multiset:

    set<A, decltype(compareA)*> Aset(compareA);

    其迭代器類型應為:pair<關鍵字類型,自定義比較類型>::iterator


    map的迭代器

    一個map的value_type是一個pair。可以改變pair的值(即改變映射關系),但不能改變關鍵字成員的值,因為它是const的。


    set的迭代器是const的

    我們在概念中說過:當解引用一個關聯(lián)容器迭代器時,會得到一個類型為容器的value_type的值的引用。但set的value_type即使它的key_type。因此,雖然set類型同時定義了iterator和const_iterator類型,但兩種類型都只允許只讀訪問set中的元素。

    與不能改變一個map元素的關鍵字一樣,一個set中的關鍵字也是const的。可以用一個set迭代器來讀取元素的值,但不能修改:



    初始化

    map and set

    • 每個關聯(lián)容器都定義了一個默認構(gòu)造函數(shù),它創(chuàng)建一個指定類型的空容器。
    • 可以將關聯(lián)容器初始化為另一個同類型容器的拷貝。
    • 或是從一個值范圍來初始化關聯(lián)容器,只要這些值可以轉(zhuǎn)化為容器所需類型就可以。

    在新標準下,我們也可以對關聯(lián)容器進行值初始化

    正如上圖所示,我們初始化map時,必須將每個關鍵字 - 值對包圍在花括號中:

    (key,value)


    multimap and multiset

    一個map或set中的關鍵字必須是唯一的,即,對于一個給定的關鍵字,只能有一個元素的關鍵字等于它。容器multimap和multiset沒有此限制,它們都允許多個元素具有相同的關鍵字。

    實例:



    關聯(lián)容器的操作

    額外的類型別名


    對于set類型,key_type和value_type是一樣的——set中保存的值就是關鍵字。

    在一個map中,元素是關鍵字-值對。即,每個元素是一個pair對象,包含一個關鍵字和一個關聯(lián)的值。由于我們不能改變一個元素的關鍵字,因此這些pair的關鍵字部分是const的:


    關聯(lián)容器和算法

    由上面的知識可知,set類型中的元素是const的,map中的元素是pair,其第一個成員是const的。不能通過迭代器更改set中的元素的值、以及map的關鍵字的值。也就意味著關聯(lián)容器只可用于只讀算法。

    但是由于只讀算法都要搜索序列。關聯(lián)容器又不能通過關鍵字遍歷查找, 因此關聯(lián)容器定義了很多特有的搜索成員,通過給定的關鍵字直接獲取元素。

    因此如果我們真要對一個關聯(lián)容器使用算法,要么將它當成一個源序列,要么將它當作一個目的位置。


    刪除元素


    刪除關鍵字的版本:對于保存不重復關鍵字的容器,erase的返回值總是0 or 1,0表示想刪除的元素并不在容器中。


    添加元素

    insert操作


    insert有兩個版本,分別接受一對迭代器,或者是一個初始化器列表,這兩個版本對于一個給定的關鍵字,只有第一個帶此關鍵字的元素才被插入到容器中:


    通過insert操作向map添加元素

    對一個map進行insert操作時,必須記住元素類型是pair。

    通常,對于想要插入的數(shù)據(jù),并沒有一個現(xiàn)成的pair對象。可以在insert的參數(shù)列表中創(chuàng)建一個pair:


    insert的返回值

    insert(或emplace)返回的值依賴于容器類型和參數(shù)。對于不包含重復關鍵字的容器,添加單一元素的insert和emplace版本返回一個pair,告訴我們插入操作是否成功。pair的first成員是一個迭代器,指向具有給定關鍵字的元素;second成員是一個bool值,指出元素是插入成功還是已經(jīng)存在于容器中。如果關鍵字已在容器中,則insert什么事情也不做,且返回值中的bool部分為false。如果關鍵字不存在,元素被插入容器中,且bool值為true。


    向兩種multi容器添加元素

    與map和set不同,一個multi容器中的關鍵字不必唯一,在這些類型上調(diào)用insert總會插入一個元素。返回一個指向新元素的迭代器。


    map的下標操作

    • map和unordered_map容器提供了下標運算符和一個對應的at函數(shù)。
    • set類型不支持下標,因為set中沒有與關鍵字相關聯(lián)的“值”。 元素本身就是關鍵字,因此“獲取與一個關鍵字相關聯(lián)的值”的操作就沒有意義了。
    • 不能對一個multimap或一個unordered_multimap進行下標操作,因為這些容器中可能有多個值與一個關鍵字相關聯(lián)。

    類似我們用過的其他下標運算符,map下標運算符接受一個索引(即,一個關鍵字),獲取與此關鍵字相關聯(lián)的值。

    但是,與其他下標運算符不同的是(對于其他下標運算符而言,訪問一個不存在地址/索引顯然是一個未定義的行為),如果關鍵字并不在map中,會為它創(chuàng)建一個元素并插入到map中,并對關聯(lián)值進行值初始化。

    用一個實例來看:

    將會執(zhí)行如下操作:

    • 在word_count中搜索關鍵字為Anna的元素,未找到。
    • 將一個新的關鍵字-值對插入到word_count中。關鍵字是一個const string,保存Anna。值進行值初始化,在本例中值為0。
    • 提取出新插入的元素(鍵值對),并將值1賦予它(它指的是鍵值對中的值)。

    由于下標運算符可能插入一個新元素,因此只可以對非const的map使用下標操作。


    下標操作的返回值

    通常情況下,解引用一個迭代器所返回的類型與下標運算符返回的類型是一樣的(如vector和string)。但對map則不然:

    • 當對一個map進行下標操作時,會獲得一個mapped_type對象;
    • 當解引用一個map迭代器時,會得到一個value_type對象。

    通過下標([])對map的key與value進行操作:

    void add_val(map<string, vector<string>>& map, const string& key,const string& s){map[key].push_back(s);// 當map[key]對應的vector<string>存在時直接將s push_back進去// 不存在時首先創(chuàng)建一個空的vector<string>,再將s push_back進去 }void add_key(map<string, vector<string>>& map, const string& key) {map[key];// 當map[key]對應的vector<string>存在時只是獲取其vector,但不做任何操作// 不存在時,在容器中為key創(chuàng)建一個對象,進行默認初始化,構(gòu)造一個空的vector//等價于:/*if(map.find(key) == map.end()){map.[key] = vector<string>();}find函數(shù)找到參數(shù)返回參數(shù)對應的迭代器,未找到返回尾迭代器。*/ } int main(int argc, char const *argv[]) {map<string, vector<string>> map;add_key(map, "li");add_key(map, "chen");add_key(map, "jiang");add_val(map, "li", "si");add_val(map, "chen", "chouchou");add_val(map, "li", "wu");add_val(map, "chen", "bianbian");for(auto& i : map){cout << i.first << ": ";for(auto& j : i.second){cout << j << " ";}cout << endl;}return 0; }

    其實map<string, vector<string>>和multimap<string, string>起到的作用是類似的。

    如果用multimap存儲的話只是不需要add_key函數(shù)了、add_value函數(shù)可以直接用insert操作。


    訪問元素

    概念



    利用find和count進行查找

    以multi容器為例,容器中如果有多個元素具有一樣的關鍵字,則這些元素在容器中會相鄰存儲。

    那么想要遍歷一個關鍵字的所有元素應該這么做:

    auto countover = multi.count(key); // 關鍵詞出現(xiàn)的次數(shù)auto findover = multi.find(key); // 具有給定關鍵字的第一個元素while (countover--) { // 循環(huán)次數(shù)為關鍵字出現(xiàn)的次數(shù)cout << findover->second << endl; // 關鍵字對應的值findover++; // 后移一位元素,相同的關鍵字相鄰排列}

    lower_bound和upper_bound面向迭代器

    lower_bound和upper_bound返回一個范圍:

    • 如果關鍵字在容器中,lower_bound返回的迭代器指向第一個具有關鍵字的元素;upper_bound返回的迭代器指向最后一個具有關鍵字的元素的后面位置。 也就是類似于begin和end迭代器組成的左閉右開區(qū)間,表示所有具有該關鍵字的元素的范圍。
    • 如果關鍵字不在容器內(nèi), 由于lower_bound和upper_bound面向有序的關聯(lián)容器,因此兩者返回的迭代器相同,都指向第一個關鍵字大于k(給定關鍵字)的元素。

    因此可以重寫上面的程序:

    for(auto beg = multi.lower_bound(key); beg != multi.upper_bound(key); beg++){cout << beg -> second << endl; }

    equal_range函數(shù)

    用equal_range函數(shù)重寫上面的程序:

    for(auto pos = multi.equal_range(key); pos.first != pos.second; pos.first++){cout << (pos.first)->second << endl; }

    對照使用lower_bound和upper_bound的代碼,可以清晰看出來pos.first等價于beg,pos.second等價于end。返回的都是指向一個multi元素的迭代器,對其解引用會得到一個multi的value_type對象——pair,因此需要通過->來訪問second——也就是“鍵-值對”中的值。


    下標和insert的區(qū)別、下標與find的區(qū)別

    []與insert不同,

    • insert: 當map的key存在時不做任何操作;不存在時插入鍵值對。
    • []: 當key存在時,用push_back將value添加進去;如果不存在,創(chuàng)建key然后用push_back將value添加進去。

    兩者在key不存在時操作是類似的。存在時,insert維護原有鍵值對(不對容器進行更改),下標使用最新的鍵值對覆蓋原有的鍵值對(對容器進行更改)。

    []與find不同:

    • []: key不存在時,構(gòu)造一個元素,將其插入到容器;存在時,獲取其元素
    • find: key不存在時,返回尾后迭代器;存在時,返回的迭代器指向第一次出現(xiàn)的關鍵字的位置

    兩者在key存在時操作是類似的。不存在時,find不做更改容器的操作,下標會對容器的內(nèi)容進行更改。



    有序容器

    關鍵字類型

    有序容器使用比較函數(shù)來比較關鍵字,從而將元素按順序存儲。

    對于有序容器——map、multimap、set以及multiset,關鍵字類型必須定義元素比較的方法。 默認情況下,標準庫使用關鍵字類型的<運算符來比較兩個關鍵字。在集合類型set中,關鍵字類型就是元素類型;在映射類型map中,關鍵字類型是元素的第一部分的類型。

    c++11允許提供自己定義的操作來代替關鍵字上的<運算符。所提供的操作必須在關鍵字類型上定義一個嚴格弱序(strict weak ordering)。可以將嚴格弱序看作“小于等于”:

    • 兩個關鍵字不能同時“小于等于”對方;如果k1“小于等于”k2,那么k2絕不能“小于等于”k1。
    • 如果k1“小于等于”k2,且k2“小于等于”k3,那么k1必須“小于等于”k3。
    • 如果存在兩個關鍵字,任何一個都不“小于等于”另一個,那么我們稱這兩個關鍵字是“等價”的。如果k1“等價于”k2,且k2“等價于”k3,那么k1必須“等價于”k3。

    如果兩個關鍵字是等價的(即,任何一個都不“小于等于”另一個),那么容器將它們視作相等來處理。當用作map的關鍵字時,只能有一個元素與這兩個關鍵字關聯(lián),我們可以用兩者中任意一個來訪問對應的值。


    重載關鍵字類型代替<運算符

    在定義關聯(lián)容器時,必須用尖括號指出要定義哪種類型的容器,自定義的比較類型必須在尖括號中緊跟著元素類型給出。

    上面說到,可以用一個具有嚴格弱序性質(zhì)的函數(shù)來起到<運算符的作用,但是函數(shù)名并不是一個類型,它僅僅只是一個名字。因此需要提供一個指向函數(shù)的指針——此時操作類型為函數(shù)指針類型。

    struct A{int age; };bool compareA(const A& a1, const A& a2){return a1.age < a2.age; }int main(int argc, char const *argv[]) {set<A, decltype(compareA)*> Aset(compareA);return 0; }

    上例中,我們使用decltype來指出自定義操作的類型。

    當用decltype來獲得一個函數(shù)指針類型時,必須加上一個*來指出我們要使用一個給定函數(shù)類型的指針。 用compareA來初始化Aset對象,這表示當我們向Aset添加元素時,通過調(diào)用compareA來為這些元素排序。即,Aset中的元素將按它們的age成員的值排序。可以用compareA代替&compareA作為構(gòu)造函數(shù)的參數(shù),因為當我們使用一個函數(shù)的名字時,在需要的情況下它會自動轉(zhuǎn)化為一個指針。 當然,使用&compareIsbn的效果也是一樣的。

    用typedef定義與compareA相容的函數(shù)指針類型,然后用此類型聲明set,起到的效果和上面是一樣的:

    typedef bool (*pf)(const A& , const A&); set<A, pf> Aset2(compareA);

    無序容器

    關鍵字類型

    與有序容器使用比較運算符來組織元素不同。無序容器使用關鍵字類型的==運算符來比較元素,還使用一個hash<key_type>類型的對象來生成每個元素的哈希值。

    不能直接定義關鍵字類型為自定義類類型的無序容器。因為不能向容器那樣直接使用哈希模板(如 hash<string>()),必須定義我們自己的hash模板版本。

    類似于有序容器重載關鍵字類型的默認比較操作。無序容器需要提供函數(shù)來替代==運算符和哈希值計算函數(shù),格式如下(以unordered_multiset為例):

    unordered_multiset<類型名, 重載的哈希函數(shù), 重載的==運算符>

    管理桶

    無序容器在存儲上組織為一組桶,每個桶保存零個或多個元素。無序容器使用一個哈希函數(shù)將元素映射到桶。為了訪問一個元素,容器首先計算元素的哈希值,它指出應該搜索哪個桶。容器將具有一個特定哈希值的所有元素都保存在相同的桶中。如果容器允許重復關鍵字,所有具有相同關鍵字的元素也都會在同一個桶中。因此,無序容器的性能依賴于哈希函數(shù)的質(zhì)量和桶的數(shù)量和大小。

    不同關鍵字的元素映射到相同的桶也是允許的。當一個桶保存多個元素時,需要順序搜索這些元素來查找我們想要的那個。計算一個元素的哈希值和在桶中搜索通常都是很快的操作。但是,如果一個桶中保存了很多元素,那么查找一個特定元素就需要大量比較操作。


    關鍵字相同的元素相鄰存儲

    不論在有序容器還是無序容器中,具有相同關鍵字的元素都是相鄰存儲的。

    總結(jié)

    以上是生活随笔為你收集整理的C++之关联容器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。