[知識點]C++中STL容器之map

UPDATE(20190416):寫完vector和set以後,發現很多內容所有引導到map上了……因而進行了必定的描述補充與更正。html

 

零、STL目錄c++

  一、容器之map算法

  二、容器之vector小程序

  三、容器之set數組

 

1、前言數據結構

  鑑於最近很多次都要用到map我卻老是出各類bug,因而決定寫一篇總結來鞏固一下。函數

  這篇文章雖名爲STL容器之map,但其實包含了整個STL及其容器的概念與用途,以及諸如vector, set等基本容器的許多常規操做,能夠做爲一篇STL容器的總領性文章。spa

 

2、什麼是STLcode

  全稱Standard Template Library,中文名標準模板庫,目的是標準化組件,屬於C++語言的一部分,說白了就是算法的整合包,最經常使用的例子即是快速排序,在C中你只能老老實實地碼出來,而C++中調用<algorithm>中的sort函數便可。htm

 

3、STL容器

  STL分爲六個部分,這裏重點介紹其中的容器(containers)部分。STL容器本質是一些經常使用的數據結構的模板,簡化操做。容器分爲以下幾種,並列出幾種最經常使用的:

    一、序列式容器(經常使用:vector/向量)

    二、適配器容器(經常使用:stack/棧,queue/隊列,priority_queue/優先隊列)

    三、關聯式容器(經常使用:set/集合,map/映射)

  今天,咱們重點介紹map,即映射。

 

4、map的用途

  I miss the taste of the sweeter life...哦對不起串了。

  從名字就很好理解做用了——映射,令一物與另外一物之間創建起一一映射的關係,以便得到將不方便查找的信息。

  舉個例子,一個很簡單的問題,讀入n個長度爲m的字符串,再詢問q次某某字符串是否在其中。初學者選擇字符一一比對,O(nmq);這是有人說,聰明人看到字符串早就去Hash了,能夠,but,爲保證正確性,哈希值一樣會很大,開一個數組去存怕是無內存上限。

  那好,咱們將獲得的哈希值編一個號,好比某巨大的數咱們將它視做1,另外一個巨大的數編成2,再把編號與值一一對應的關係給記錄下來,用什麼記錄?

  map:正是在下。

 

5、map的聲明與功能

  上面提了,map用途在於自動創建編號與值的對應。那麼STL提供了哪些函數供咱們來搗鼓裏面的東西呢?

  函數清單:insert, begin, end, size, rbegin, rend, find, count, lower_bound, upper_bound, erase。

  其實這些函數中大部分也是STL容器中通用的函數,下面會有詳細的使用介紹,適用於但不只限於map。

 

一、構造

map <int, string> a;

  簡單明瞭,前者爲鍵值(key),後者爲值(value),此處爲整形&字符串的映射,二者亦可爲任何其餘支持的類型。

 

二、插入數據

  方法一(以pair形式):

a.insert(pair <int, string>(1, "Zhangsan")); 
a.insert(pair <int, string>(2, "Lisi")); 
a.insert(pair <int, string>(3, "Wangwu")); 

  方法二(以value_type形式):

a.insert(map <int, string> :: value_type(1, "Zhangsan"));  

  方法三(以數組形式):

a[1] = "Zhangsan";

  須要注意的是,方法一二沒法覆蓋數據,而方法三能夠,即若是鍵值爲1的map已經被映射到「Zhangsan」,能夠經過數組形式直接修改。

 

三、數據遍歷

  這裏引入新概念——迭代器(iterator)。先聲明:

map <int, string>::iterator it; 

  使用方法也很簡易:

for (it = a.begin(); it != a.end(); it++)  
   cout << it -> first << ' ' << it -> second << endl;

  這個是前向迭代器,一樣還有反向迭代器,以下:

map <int, string> :: reverse_iterator rit;

for
(rit = a.rbegin(); rit != a.rend(); rit++) cout << rit -> first << ' ' << rit -> second << endl;

  一樣地,還有用數組的方式遍歷:

int s = a.size();
for (int i = 1; i <= s; i++) 
    cout << i << ' ' << a[i] << endl;

  其中,size函數是用來獲取map中有多少項數據。

  注意點:在遍歷時咱們用了begin()和end(),二者類型均爲迭代器。咱們發現遍歷起始點爲begin(),當迭代器不等於end()時繼續,這意味着begin()爲開頭標識,它包括了數據內容,即第一項數據;end()爲結尾標識,但它是最後一項數據後面的一個獨立的標識,自己並不表示任何數據。

 

四、查找數據

  ①判斷是否出現用count函數

  ②判斷出如今哪裏用find函數

map <int, string> :: iterator itx;
itx = map.find(1);
int x = map.count(1);

map <int, string> :: iterator ity;
ity = map.find(4);
int y = map.count(4);

  count函數只返回0或1,如上述代碼中,x = 1, y = 0;

  find函數返回的是一個迭代器,如上述代碼中,itx -> first = 1, itx -> second = "Zhangsan";

  而若是沒有該數據的話,則返回的是it = a.end()(末尾標識)。

 

五、上界下界

  lower_bound()返回要查找關鍵字的下界,即值 >= 給定元素的第一個位置,upper_bound()返回上界,即值 > 給定元素的第一個位置。

 1 map <int, int> a;
 2 
 3 int main() {
 4     a[1] = 1, a[2] = 2, a[3] = 3, a[4] = 4;
 5     map <int, int> :: iterator it;
 6     for (int i = 1; i <= 4; i++) {
 7         it = a.lower_bound(i);
 8         printf("%d ", it -> first);
 9     }
10     for (int i = 1; i <= 4; i++) {
11         it = a.upper_bound(i);
12         printf("%d ", it -> first);
13     }
14     return 0;
15 }

  輸出結果爲「1 2 3 4 2 3 4 4」,其中upper_bound(4)因爲不存在,故it = a.end(),而爲何it -> first輸出4,這是個哲學問題,引用哲學家Cab的話說,「從結論上說訪問end的first是會獲得size,然而是undefined behavior(未定義行爲)」。因此大可沒必要糾結這東西了。

 

六、刪除元素

  這裏介紹兩種刪除方法:單刪除,區間刪除。

  單刪除即直接刪除一項,傳入參數能夠爲鍵值,也能夠爲一個迭代器。

map <int, string> :: iterator it;
a.erase(1);
it = a.find(2);
a.erase(it);

  區間刪除只能爲迭代器。聽上去美,可是畢竟這是map不是for,當你的數據並非美好的從1到n連續編號時,你只能拿它做clear的操做了,也就是:

a.erase(a.begin(), a.end());

  從這裏又可得知一點,erase兩個參數也是左閉右開的關係,即包括前者不包括後者。

 

七、排序問題

  map中的元素是自動按鍵值升序排序,不管是數仍是字符串。但若是鍵值爲一個結構體,咱們須要重載小於符號。以下小程序中,咱們將囊括學號姓名兩項數據的結構體做爲鍵值,成績做爲值,而後須要以學號進行升序排序:

#include <bits/stdc++.h>
using namespace std;

#define MAXN 35

struct Stu {
    int id;
    string str;
    bool operator < (Stu const& x) const {
        return id < x.id;
    }
};

map <Stu, int> a;

int n, id, o;
string str;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> id >> str >> o;
        a.insert(pair <Stu, int> ((Stu) {id, str}, o));
    }
    map <Stu, int> :: iterator it;
    for (it = a.begin(); it != a.end(); it++)
        cout << it -> first.id << ' ' << it -> first.str << ' ' << it -> second << endl;
    return 0;
}

 

6、小結

  STL廣泛有一個前閉後開的特性,這裏有兩處均能體現:一處是關於begin()和end()的定義,一處是erase()的區間刪除方式,之後應該還會接觸到更多。

  平時STL容器用的不是太多,也就queue, priority_queue, map較爲經常使用。算法卻是常常用(algorithm庫),之後有機會再慢慢總結STL的其餘部分。

相關文章
相關標籤/搜索