一、概述
1、是什麼
map是一類關聯式容器,關聯的本質在於元素值與某個特定的鍵相關聯。增刪節點對迭代器影響很小,對於迭代器而言,不可修改鍵,只能修改其對應的值。map內部自建一棵紅黑樹,對內部元素有自動排序的功能。
紅黑樹:一種二叉查找樹,此外在每個節點上增加一個存儲位表示節點的顏色,可以是red,也可以是black。通過對一條從根到葉子節點的路徑上各個節點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長兩倍,因此是接近平衡的。作爲一個二叉查找樹,滿足二叉查找樹的一般性質。
二叉查找樹,即有序二叉樹,有如下性質:
- 任意節點的左子樹不空,則左子樹上所有節點的值均小於它的跟節點的值
- 任意節點的右子樹不空,則右子樹上所有節點的值均大於它的跟節點的值
- 任意節點的左、右子樹也分別是二叉查找樹
- 沒有鍵值相等的節點
二叉查找樹高度爲lgn,一般操作執行時間爲O(logn),如果二叉查找樹退化爲一棵具有n個節點的線性鏈後,則這些操作最壞情況的運行時間爲O(n)。而紅黑樹增加了着色和相關性質,是的紅黑樹相對平衡,從而保證了紅黑樹的查找、插入、刪除的時間複雜度最壞爲O(logn)。
紅黑樹性質:
- 每個節點要麼是red,要麼是black
- 跟節點是black
- 每個葉子結點都是black
- 如果一個節點是red,那麼它的兩個兒子都是black
- 對於任意節點而言,其到葉子結點的每條路徑都包含相同數目的black節點
這5個性質保證了一棵有n個節點的紅黑樹始終保持logn的高度。
2、功能
首先,使用map需要包含頭文件#include<map>。自動簡歷key-value的一一對應關係,key和value可以是任意類型,但是key的類型需要支持<操作符。由上述可知,查找的複雜度基本是logn,如果有1000個記錄,則最多需要查詢10次,如果有1000000個記錄,最多需要查詢20次。當然也有添加、刪除、修改value、遍歷等功能。
二、map的使用
1、插入
map<int, string> m_str;
pair<map<int, string>::iterator, bool> is_suc;
//map的插入有三種方式
//1、insert(pair<>)
is_suc = m_str.insert(pair<int, string>(2, "yuhy"));
//可以用is_suc判斷是否插入成功
if(is_suc.second){
cout << "successful" << endl;
}else{
cout << "failed" << endl;
}
//2、insert (value_type)
m_str.insert(map<int, string>::value_type(5, "scott"));
//3、用數組方式插入數據
m_str[1] = "LiMing";
2、遍歷
//遍歷方式四種:
//1、for each
for(auto it : m_str){
cout << "key:" << it.first << ", value:" << it.second << endl;
}
cout << "-------" << endl;
//2、前向迭代器
for(auto it = m_str.begin(); it != m_str.end(); it++){
cout << "key:" << it->first << ", value:" << it->second << endl;
}
cout << "-------" << endl;
//3、反向迭代器
for(auto it = m_str.rbegin(); it != m_str.rend(); it++){
cout << "key:" << it->first << ", value:" << it->second << endl;
}
cout << "-------" << endl;
//4、數組遍歷方式
for(int i=0; i<m_str.size(); i++){
cout << "value:" << m_str[i] << endl;
}
3、訪問
//operator[]和at()
map<int, int> t_map;
t_map[2] = 2;
t_map[4] = 4;
t_map[6] = 6;
cout << "key=2, value=" << t_map.at(2) << endl;
for(auto it : t_map){
cout << "key:" << it.first << ",value:" << it.second << endl;
}
t_map.at(2) += 1;
cout << "key=8, value=" << t_map[8] << endl;
for(auto it : t_map){
cout << "key:" << it.first << ",value:" << it.second << endl;
}
operator[]和at()的區別:
使用map[key],如果key不存在
,則會自動爲您添加一個鍵,並對值進行初始化。
使用map.at(key), 會檢查key,如果key不存在則會跑出異常。
4、查找
//查找,兩種
//1、count,查詢關鍵字是否出現,無法判定其位置,返回值0或者1,1表示存在
cout << "5 是否出現 :" ;
if(m_str.count(5) == 1){
cout << "是";
}else{
cout << "否";
}
cout << endl;
//2、find,返回值是迭代器,如果不存在則返回end()
map<int, string>::iterator res_find = m_str.find(5);
if(res_find == m_str.end()){
cout << "yuhy 不存在" << endl;
}else{
cout << "yuhy key:" << res_find->first << ", value:" << res_find->second << endl;
}
5、刪除
//刪除
//1、erase(k),刪除鍵爲k的元素,並返回刪除的個數
map<int, string>::size_type res_erase = m_str.erase(5);
cout << "num of erase:" << res_erase << endl;
//2、erase(it),刪除迭代器it指向的元素
m_str.erase(m_str.find(1));
//3、erase(it_f, it_e),刪除一段範圍內的元素
m_str.erase(m_str.begin(), m_str.end());
6、排序
map默認key是從小到大排序。
map定義:
template<class Key,
class T,
class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>
>
class map;
有四個參數,第一個是key,第二個是value,第三個參數提供了默認的less,less是STL中的一個函數對象,調用操作符的類,其對象常常稱爲函數對象,他們是行爲類似函數的對象。表現出一個函數的特徵,就是通過“對象名 + (參數列表)”的方式使用一個類,其實質是對operator()操作符的重載。
less的實現:
template <class T>
struct less : binary_function< T , T, bool>{
bool operator()(const T& x, const T& y) const{
return x < y;
}
};
裏面僅僅是對()運算符的重載,調用的是T的<運算符。當然,與less相對的還有greater。
方式一:如果key是自定義對象,重寫<操作符
class Student{
public:
std::string name_;
int age_;
Student(std::string name, int age):name_(name),age_(age){}
Student(){}
bool operator < (const Student &s) const {
std::cout << "operator : < " << std::endl;
if(name_ != s.name_){
return name_ < s.name_;
}else{
return age_ < s.age_;
}
}
};
map<Student, string> m_stu;
m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
cout << "--- 排序 ---" << endl;
for(auto it : m_stu){
cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
}
方式二、重載()
class Comp{
public:
bool operator () (const Student &left, const Student &right) const {
std::cout << "operator ()" << std::endl;
if(left.name_ != right.name_){
return left.name_ < right.name_;
}else{
return left.age_ < right.age_;
}
}
};
map<Student, string, Comp> m_stu;
m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
cout << "--- 排序 ---" << endl;
for(auto it : m_stu){
cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
}
方式三:
class Student{
public:
std::string name_;
int age_;
Student(std::string name, int age):name_(name),age_(age){}
Student(){}
std::string show(){
return "name:" + name_ + ", age:" + std::to_string(age_);
}
bool operator < (const Student &s) const {
std::cout << "operator : < " << std::endl;
if(name_ != s.name_){
return name_ < s.name_;
}else{
return age_ < s.age_;
}
}
};
bool comp (const Student &left, const Student &right){
cout << "comp method" << endl;
if(left.name_ != right.name_){
return left.name_ < right.name_;
}else{
return left.age_ < right.age_;
}
}
map<Student, string, decltype(comp)*> m_stu(comp);
m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
cout << "--- 排序 ---" << endl;
for(auto it : m_stu){
cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
}
方式四:對map中的value進行排序
首先想到的是STL中的sort算法,但是sort只能對序列容器進行排序,只能是線性的(如vector、list、deque)。map是個集合容器,存儲的元素是pair,不是線性存儲的,所以不能直接用sort。
但是我們可以將map中的元素放到序列容器中,再對序列容器中的元素進行排序。有一個必要條件,序列容器中的元素必須是可比較的,即實現了<操作的。
在使用sort的時候,傳入比較函數,實現對pair中的value進行比較。
bool cmp_value(const pair<Student, string>& a, const pair<Student, string>& b) {
return b.second < a.second;
}
map<Student, string, decltype(comp)*> m_stu(comp);
m_stu.insert(pair<Student, string>(Student("yyy", 20), "yyy"));
m_stu.insert(pair<Student, string>(Student("aaa", 20), "aaa"));
m_stu.insert(pair<Student, string>(Student("yyy", 18), "yyy"));
cout << "--- 排序 ---" << endl;
for(auto it : m_stu){
cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
}
vector<pair<Student, string>> vec(m_stu.begin(), m_stu.end());
sort(vec.begin(), vec.end(), cmp_value);
for(auto it : vec){
cout << "key-name:" << it.first.name_ << ", key-age:" << it.first.age_ << ", value:" << it.second << endl;
}