1. 程式人生 > 其它 >【STL】關聯容器之unordered_map用法總結

【STL】關聯容器之unordered_map用法總結

技術標籤:C++unordered_map雜湊表c++

一、基本原理

unordered_map是C++標準庫提供的關聯容器之一,儲存的是鍵值對(key-value),我們可以通過key快速查詢到其對應的value。unordered_map底層使用的資料結構是雜湊表(hash table),因此在unordered_map中查詢、新增或刪除元素時間複雜度都是常數時間O(1)。此外,unordered_map中的元素是無序的


unordered_map使用場景:

現在假設有一個寢室501,寢室有4個學生,我們要用一種容器來儲存這4個學生的學號和姓名,需求是我要通過學號來查詢這個學號對應的姓名。

此時我們用一個unordered_map來儲存,學號作為key,姓名作為value,一個學生的這兩個資訊作為unordered_map的一個元素pair(pair用法總結),我們可以通過學號來得到對應的姓名。

也許你會有這樣的疑問,為什麼要用unordered_map呢,我用一個二維陣列不是也一樣能存放寢室4個學生的學號和姓名嗎?

是的,存是可以存,但是操作的時間複雜度就不一樣了,比如我知道學號"sc0303",我想查詢對應的姓名,如果用unordered_map儲存的,根據雜湊表的特性,只需要O(1)的時間就能查到對應的姓名。如果用的二維陣列儲存的,需要從頭遍歷二維陣列的第一列,需要O(n)的時間才能查到。這就是使用unordered_map的意義。

也許你還會有這樣的疑問,為什麼要用unordered_map呢,我用一個map也一樣能存嗎?

用map和unordered_map的區別在於,map基於紅黑樹,是按關鍵字有序排列的,unordered_map基於雜湊表,是雜湊存放的(無序)。當你需要進行範圍查詢“比學號sc0302大的同學的姓名”,那麼使用map就更方便,因為map中元素已經按照學號(關鍵字)有序排好了,但是如果這種場景下使用unordered_map,你要進行範圍查詢只能一個一個去比較了。在進行等值查詢時unordered_map只需要O(1)的時間就能查到,但map卻需要O(log(n))的時間,因此等值查詢時使用unordered_map更快。

具體選用哪種容器還是要看使用場景,它們各有優勢。

二、用法

unordered_map中的元素是一對對鍵值對,型別pair,用法可參考部落格pair用法總結

初始化

unordered_map<T1,T2> 容器名;T1、T2是型別名,可以是基本資料型別int、double等,也可以是類型別string等
unordered_map<T1,T2> m1;建立一個名為m1的空unordered_map,key的型別為T1,value的型別為T2
unordered_map<T1,T2> m1{p1,p2……};m1中的元素被初始化為p1,p2……,p1、p2是pair型別
unordered_map<T1,T2> m1{{key1,value1},{key2,value2}……};m1中的元素被初始化為 鍵值對{key1,value1},{key2,value2}……
unordered_map<T1,T2> m2(m1);m2中包含和m1一樣的元素
unordered_map<T1,T2> m2=m1;m2中包含和m1一樣的元素

程式示例:

pair<string, string> p1("sc0301","小楊");	            // 方式一,建立一個pair名為p1
pair<string, string> p2 = make_pair("sc0302", "小馬");	// 方式二,make_pair函式返回一個用"sc0302"和 "小馬"初始化的pair
pair<string, string> p3("sc0303", "小王");
pair<string, string> p4("sc0304", "小何");

unordered_map<string, string> m1;                 // 建立一個空unordered_map
unordered_map<string, string> m2{ p1,p2,p3,p4 };  // 建立一個包含鍵值對p1、p2、p3、p4的unordered_map
unordered_map<string, string> m3{ {"sc0301","小楊"},{"sc0302", "小馬"},{"sc0303", "小王"},{"sc0304", "小何"} }; // 效果同上一句
unordered_map<string, string> m4(m2);   // 建立一個unordered_map,m4中包含和m2一樣的元素
unordered_map<string, string> m5 = m2;  // 建立一個unordered_map,m5中包含和m2一樣的元素

訪問元素

訪問元素
T2 value = m1[key];得到關鍵字key對應的值value
m1.at(key);得到關鍵字key對應的值value
*iter訪問迭代器iter指向的元素
獲取迭代器
m1.begin();獲取指向m1首元素的迭代器
m1.end();獲取指向m1尾元素的後一個位置的迭代器
m1.rbegin();獲取指向m1首元素的前一個位置的迭代器
m1.rend();獲取指向m1尾元素的迭代器
m1.cbegin(); m1.cend();含義同上,但獲取到的是const_iterator
m1.crbegin(); m1.crend();含義同上,但獲取到的是const_iterator

程式示例:

string p2_name = m2["sc0302"];	   // 得到學號(關鍵字)"sc0302"對應的姓名(值)
string p3_name = m2.at("sc0303");  // 得到學號"sc0303"對應的姓名	      

unordered_map<string, string>::iterator it1 = m2.begin();	 // 得到指向m2首元素的迭代器
unordered_map<string, string>::iterator it2 = m2.end();    // 得到指向m2尾元素的下一個位置的迭代器
pair<string, string> p11 = *it1;   // 得到m2的首元素{"sc0301","小楊"}
string p1_ID = it1->first;         // 得到m2的首元素{"sc0301","小楊"}的fisrt成員
string p1_name = it1->second;	   // 得到m2的首元素{"sc0301","小楊"}的second成員

遍歷容器

1、使用迭代器

for (auto it_b = m2.begin(), it_e = m2.end(); it_b != it_e;++it_b) {
	pair<string, string> current = *it_b;
	cout << "學號:" << it_b->first << "; 姓名:" << it_b->second << endl;
	// 上面這一句等同於下面這一句
	cout << "學號:" << current.first << "; 姓名:" << current.second << endl;
}

2、範圍for語句

for (auto p : m2) {
	cout << "學號:" << p.first << "; 姓名:" << p.second << endl;
}

新增、修改元素

m1.insert(p1);在unordered_map中插入已有的pair
m1.insert({key1,value1});在unordered_map中插入鍵值對{key1,value1}
m1.insert(pair<T1, T2> (key1, value1));// 建立一個無名pair物件,並插入到unordered_map中
m1.insert(make_pair(key1, value1));// 建立一個無名pair物件,並插入到unordered_map中
m1.insert(unordered_map<T1, T2>::value_type (key1, value1));// 建立一個無名pair物件,並插入到unordered_map中
m1[key] = value;如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value
m1.emplace(p1);在unordered_map中插入已有的pair
m1.emplace(pair<T1, T2> (key1, value1));// 建立一個無名pair物件,並插入到unordered_map中

程式示例:

m1.insert(p1);	                   // 在unordered_map中插入已有的pair
m1.insert({ "sc0302", "小馬" });   // 插入鍵值對{ "sc0302", "小馬" }
m1.insert(pair<string, string> ("sc0303", "小王"));  // 建立一個無名pair物件,並插入到unordered_map中	
m1.insert(make_pair("sc0303", "小王"));              // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.insert(unordered_map<string, string>::value_type("sc0303", "小王"));   // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1["sc0304"] = "小何";             // 如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value。

m1.emplace(p1);			                             // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.emplace(pair<string, string>("sc0303", "小王"));  // 要插入的關鍵字已在容器中,emplace/insert什麼都不做

刪除元素

m1.erase(key)刪除關鍵字為key的元素
m1.erase(iter);刪除迭代器iter指向的元素
m1.erase(iter1,iter2);刪除迭代器iter1和ter2指向的範圍中元素
m1.clear();刪除m1中的所有元素

程式示例:

m1.erase("sc0301");		  // 刪除關鍵字為"sc0301"的元素
auto iter = m1.begin();   // 指向m2的首元素的迭代器
m1.erase(iter);           // 刪除迭代器指向的元素
auto iter1 = m1.begin(), iter2 = m1.end();
m1.erase(iter1, iter2);   // 刪除迭代器iter1、iter2指向的範圍中的元素
m2.clear();               // 刪除容器中的全部元素

unordered_map的大小

m1.szie();獲得m1的大小
m1.empty();若m1為空返回值為true,否則返回值為false

程式示例:

int s = m3.size();    // 獲得m1的大小
bool e = m3.empty();  // 若m1為空返回值為true,否則返回值為false

查詢

m1.find(key)返回一個迭代器,指向第一個關鍵字為key的元素,若key不在容器中,則返回尾後迭代器
m1.count(key);返回關鍵字等於key的元素的數量。對於不允許重複關鍵字的容器unordered_map,返回的值是0或1

程式示例:

unordered_map<string, string>::iterator it = m4.find("sc0301");  // 查詢關鍵字為"sc0301"的元素,返回一個迭代器
if (it == m4.end()) {         // 若"sc0301"不在容器中,則it等於尾後迭代器
	cout << "未找到!" << endl;
}
else {
	pair<string, string> result1 = *it;	// 找到了
}

int result2 = m4.count("sc0305");  // 查詢關鍵字為"sc0301"的元素,返回關鍵字等於"sc0301"的元素數量
if (result2==0) {
	cout << "未找到!" << endl;
}
else {
	cout << "找到了!" << endl;
}

unordered_map<string, string>::iterator it_up = m4.upper_bound("sc0301");  // 返回一個迭代器,指向第一個關鍵字大於"sc0301"的元素
pair<string, string> result3 = *it_up;

三、程式示例

#include <iostream>
#include <string>
#include <utility>
#include <unordered_map>

using namespace std;

int main() {	
	// 初始化-----------------------------------------------------------------------------------
	pair<string, string> p1("sc0301","小楊");	            // 方式一,建立一個pair名為p1
	pair<string, string> p2 = make_pair("sc0302", "小馬");	// 方式二,make_pair函式返回一個用"sc0302"和 "小馬"初始化的pair
	pair<string, string> p3("sc0303", "小王");
	pair<string, string> p4("sc0304", "小何");

	unordered_map<string, string> m1;                 // 建立一個空unordered_map
	unordered_map<string, string> m2{ p1,p2,p3,p4 };  // 建立一個包含鍵值對p1、p2、p3、p4的unordered_map
	unordered_map<string, string> m3{ {"sc0301","小楊"},{"sc0302", "小馬"},{"sc0303", "小王"},{"sc0304", "小何"} }; // 效果同上一句
	unordered_map<string, string> m4(m2);   // 建立一個unordered_map,m4中包含和m2一樣的元素
	unordered_map<string, string> m5 = m2;  // 建立一個unordered_map,m5中包含和m2一樣的元素

	// 訪問元素-----------------------------------------------------------------------------------
	string p2_name = m2["sc0302"];	   // 得到學號(關鍵字)"sc0302"對應的姓名(值)	
	string p3_name = m2.at("sc0303");  // 得到學號"sc0303"對應的姓名	      
	
	unordered_map<string, string>::iterator it1 = m2.begin();	 // 得到指向m2首元素的迭代器
	unordered_map<string, string>::iterator it2 = m2.end();    // 得到指向m2尾元素的下一個位置的迭代器
	pair<string, string> p11 = *it1;   // 得到m2的首元素{"sc0301","小楊"}
	string p1_ID = it1->first;         // 得到m2的首元素{"sc0301","小楊"}的fisrt成員
	string p1_name = it1->second;	   // 得到m2的首元素{"sc0301","小楊"}的second成員

	// 遍歷容器-----------------------------------------------------------------------------------
	for (auto it_b = m2.begin(), it_e = m2.end(); it_b != it_e;++it_b) {
		pair<string, string> current = *it_b;
		cout << "學號:" << it_b->first << "; 姓名:" << it_b->second << endl;
		// 上面這一句等同於下面這一句
		cout << "學號:" << current.first << "; 姓名:" << current.second << endl;
	}

	for (auto p : m2) {
		cout << "學號:" << p.first << "; 姓名:" << p.second << endl;
	}

	// 新增、修改元素-----------------------------------------------------------------------------------
	m1.insert(p1);	                   // 在unordered_map中插入已有的pair
	m1.insert({ "sc0302", "小馬" });   // 插入鍵值對{ "sc0302", "小馬" }
	m1.insert(pair<string, string> ("sc0303", "小王"));  // 建立一個無名pair物件,並插入到unordered_map中	
	m1.insert(make_pair("sc0303", "小王"));              // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
	m1.insert(unordered_map<string, string>::value_type("sc0303", "小王"));   // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
	m1["sc0304"] = "小何";             // 如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value。

	m1.emplace(p1);			                             // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
	m1.emplace(pair<string, string>("sc0303", "小王"));  // 要插入的關鍵字已在容器中,emplace/insert什麼都不做

	// 刪除元素-----------------------------------------------------------------------------------
	m1.erase("sc0301");		  // 刪除關鍵字為"sc0301"的元素
	auto iter = m1.begin();   // 指向m2的首元素的迭代器
	m1.erase(iter);           // 刪除迭代器指向的元素
	auto iter1 = m1.begin(), iter2 = m1.end();
	m1.erase(iter1, iter2);   // 刪除迭代器iter1、iter2指向的範圍中的元素
	m2.clear();               // 刪除容器中的全部元素

	// 大小-----------------------------------------------------------------------------------
	int s = m3.size();    // 獲得m1的大小
	bool e = m3.empty();  // 若m1為空返回值為true,否則返回值為false

	// 查詢-----------------------------------------------------------------------------------
	unordered_map<string, string>::iterator it = m4.find("sc0301");  // 查詢關鍵字為"sc0301"的元素,返回一個迭代器
	if (it == m4.end()) {         // 若"sc0301"不在容器中,則it等於尾後迭代器
		cout << "未找到!" << endl;
	}
	else {
		pair<string, string> result1 = *it;	// 找到了
	}

	int result2 = m4.count("sc0305");  // 查詢關鍵字為"sc0301"的元素,返回關鍵字等於"sc0301"的元素數量
	if (result2==0) {
		cout << "未找到!" << endl;
	}
	else {
		cout << "找到了!" << endl;
	}
}