map和set的簡單使用

做編程題的時候我們經常會苦於無法建立合適的索引快速找到數據,比如這樣的場景:要求我們建立學號和成績對應的數據庫,並且查詢指定學生的成績。在學生學號均由較短的數字構成時,數組或許可以滿足需求,而一個學生的學號可能包含字母,或者要求數據庫實現一些其他更復雜的操作時,數組就不那麼好用了,這時map和set就有了用武之地。

map的概念與python中的字典有點相似,而set又類似於集合。這裏我們重點介紹關鍵詞有序保存元素且關聯詞不可重複出現的關聯容器類型map和set,關鍵詞可以重複出現的multimap和multiset以及用哈希函數組織的無序集合不在本文討論之列。

一、關聯容器的創建
創建map和set前,需要添加頭文件:

//map和set的頭文件
#include<map>
#include<set>
//方便敘述添加的
#include<iostream>
using namespace std;

map是關鍵詞-值對的集合,而set只包含一個關鍵詞。
例如:

map<string, int> stu_info; //例1
set<string> stu_set;  //例2

例1建立了名爲stu_info的map,它的關鍵詞是string,值是int,而例2建立的stu_set僅有一個關鍵詞string。map和set的關鍵詞不能重複出現。
map和set的關鍵詞類型不是任意選取的,我們可以用string,int甚至double作爲關鍵詞,但是一個自己定義的結構體就做不到。核心在於關鍵詞的類型必須支持比較操作,也就是說,如果我們想要讓自己定義的類型充當map和set的關鍵詞,那麼我們就要定義自己的操作充當<運算符的功能。(這其實暗示了我們一個信息,map和set的關鍵詞是有順序的。)
我們可以對map和set進行列表初始化,初始化的方法是,對每一個關鍵詞-值對,用{}括起來:

map<string,int> stu_info={{"Sally",90},{"Jimmy",92},{"Amy",85}};
set<string> stu_set={"Sally","Jimmy","Amy"};

map的關鍵詞-值對事實上是pair類型

二、向關聯容器添加元素
1. 用insert添加
insert既可以批量添加也可以逐個添加,這裏我們僅用C++ primer第五版第384頁的代碼舉例:
逐個添加:

set<int> set2;
set2.insert({5}); //{}加不加都可以

批量添加:

vector<int> ivec ={2,4,5,8,2,4,6,8};
set<int> set2;
set2.insert(ivec.cbegin(),ivec.cend()); //set2中有4個元素
set2.insert({1,2,5,7,1,3,5,7});  //set2中有8個元素

批量添加既可以把元素的字面值放在大括號中添加,也可以用迭代器添加。從中我們也可以發現:對set添加其中已有的元素,對它是沒有影響的。
map與set類似,不同的是map中元素的基本單位是pair,其他都與set大同小異。
2. 用下標添加
map可以用下標添加,set不可以。用下標添加形式上和數組的賦值差不多,比如:

stu_info["Sally"]=92;
stu_info["John"]=90;

這種添加方式可以修改map中已有關鍵詞對應的值,如果關鍵詞在map中不存在,也會添加關鍵詞,添加後會對值進行初始化,對於這一例子會初始化爲0。

三、查詢或遍歷關聯容器中的元素
1. 用下標查詢

cout<<stu_info["Sally"];

顯然,對於map可以用下標查詢,用set不可以。但是用下標查詢有一個副作用,那就是它有可能插入新的元素,在確認查詢的值一定存在於map中時,可以用這種方法,但是如果想判斷關鍵詞在不在map中,這是不太合適的。
2. 用find查詢

set2.find(3);
stu_info.find("Sally");

find會返回一個迭代器,如果查找的關鍵詞在關聯容器中,會返回指向關鍵詞的迭代器,如果不在關聯容器中,會返回尾後迭代器,因此,用find判斷關鍵詞在不在關聯容器中的方法是:

if(set2.find(3)==set2.cend()) cout<<"The number doesn't exist!";

如果要取出迭代器對應的元素,對map:

auto iter=stu_info.find("Sally");
cout<<iter->first<<endl<<iter->second;

first取出的是關鍵詞,second取出的是值。
對set:(一般沒有必要這樣取出set的值)

auto iter2=set2.find(3);
cout<<*iter2<<endl;

可以對迭代器進行遞增遞減操作,因此可以通過這種方式逐個遍歷關聯容器:

for(auto iter3=stu_info.cbegin();iter3!=stu_info.cend();++iter3)
      cout<<iter3->first<<" "<<iter3->second<<endl;

四、刪除元素
刪除元素和插入元素中的insert一樣,只要給出關鍵詞或是迭代器,就可以逐個或是批量刪除,只要將insert換成erase就可以了。
例如:

stu_info.erase("Sally");
stu_info.erase(iter);

如果給erase傳遞的參數是關鍵詞,那麼返回的回事一個size_type的值,指出刪除元素的數量。如果給erase傳遞的參數是迭代器,無論是傳遞用一個參數指向要刪除的元素的迭代器還是用兩個參數指示刪除範圍的迭代器,返回值都是迭代器,前者返回stu_info.cend(),後者返回迭代器的尾元素(即傳遞的第二個參數)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章