map的底层实现是红黑树,map是有序的,增删查改一个元素的时间复杂度都是O(log n),使用迭代器遍历map的时间复杂度是O(n)

map的标准定义如下:

1 template < class Key,                                     // map::key_type
 2            class T,                                       // map::mapped_type
 3            class Compare = less<Key>,                     // map::key_compare
 4            class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
 5            > class map;

map中的的键和值都可以使用用户自定义的数据类型,键的比较也可以被自定义。

自定义举例如下,按Mykey中的key值的绝对值从大到小排序,如果key是基本数据类型且不需要特殊排序方式,则不用自定义排序方式。

1 typedef struct Key{
 2     int key;
 3 } MyKey;
 4 
 5 typedef struct Value{
 6     string value;
 7 } MyValue;
 8 
 9 typedef struct Comp{
10     bool operator()(const MyKey & k1, const MyKey & k2){
11         return abs(k1.key) > abs(k2.key);
12     }
13 } MyComp;
14 
15 typedef map<MyKey, MyValue, MyComp> MyMap;

 声明一个map常用方法有以下四种,声明空map,枚举元素(可以用makepair代替),复制构造函数,利用迭代器实现部分复制

1 map<int, string> a; // 声明空map
2 map<int, string> b{{1, "aaa"}, {2, "bbb"}}; // 枚举初始化元素
3 map<int, string> c(b); // 复制构造函数
4 map<int, string> d(begin(b),end(b)); // 通过迭代器实现按区间复制

map中键是唯一的,以上声明方式中的第二种,如果出现重复的key,后出现的不会覆盖前出现的,而是会以前出现的为准。如下所示

1 map<int, string> e{{1, "aaa"}, {1, "bbb"}};
2 cout << e.size() << endl; // 1
3 cout << e.find(1)->second; // "aaa"

 插入。前三者是等价的,利用前三种方法插入一个键值对,如果插入的键已存在,编译器会报错,而第四种写法会直接覆盖原先的键值对。

1 a.insert(pair<int, string>(1,"aaa"));
2 a.insert(make_pair<int, string>(2,"bbb"));
3 a.insert(map<int, string>::value_type(3,"ccc"));
4 a[4] = "ddd";

查找。注意,在使用[]运算符返回map中的一个键对应的值时,需要先判断值该键值对是否存在。当使用[]时即使查找的键不存在编译器也不会报错,而是返回垃圾值。

通常可以使用count和find方法判断一个键是否存在,由于map内部时有序的所以不需要遍历全部键值对。count返回个数,find返回找到的元素的位置,如果找不到返回map的末尾位置。以下展示了两种方法寻找键值对,第二种方法更好,因为第一种方法找了两遍。[]运算符可以使用at成员函数代替。

1 if(a.count(2) > 0) // 方法一
2     return a[2];
3 
4 auto iter1 = a.find(2);// 方法二
5 if(iter != end(a))
6     return iter->second;

修改。可以使用直接赋值修改或者使用迭代器修改。前者需要先判断是否存在在修改,后者不需要。另外,map的迭代器支持++运算但是不支持+i运算,只能根据键修改值,而不能修改键(必须要删除后重新插入)

1 a[4] = "ddd"; // 方法一
2 
3 auto iter2 = ++a.begin(); // 方法二
4 iter2->second = "eee";

删除,修改一个元素到红黑树的时间复杂度O(log N)

1 a.erase(1); // 删除指定键值的元素
2 a.erase(a.begin()); // 删除迭代器指向的元素
3 a.erase(a.begin(), a.end()); // 删除迭代器区间内的元素
4 a.clear(); // 清空所有元素

使用迭代器遍历红黑树时间复杂度O(N)

方法一,使用传统的迭代器遍历,map的迭代器属于双向迭代器,只支持向前加一或者向后减一的操作。

方法二,如果要遍历整个map而不是某一部分,可以使用C++11中引入的按范围的for循环。按范围的for循环中的it迭代器本质上是映射f的右值引用。因此利用it取键值要用 . 运算符而不是 ->

1 map<int, string> f{{1,"a"}, {2,"b"}, {3,"c"}};
2 
3 for(auto iter = f.begin(); iter != f.end(); ++iter)
4     cout << iter->first << " : " << iter->second << endl; // 方法一
5 
6 for(auto it : f)
7     cout << it.first << " : " << it.second << endl; // 方法二

其他操作

size():返回map的大小

empty():返回一个bool值判断map是否为空

swap(map1, map2):无返回值,swap是用来交换两个map的内容而不是用来交换map中的内容。

equal_range(iter1, iter2, val):以pair的形式返回一对迭代器,返回得迭代器的范围等于iter1和iter2中值等于val的范围

lower_bound(key):返回map中第一个大于等于key的迭代指针

upper_bound(key):返回map中第一个大于key的迭代指针

emplace(key, val):生成一个pair(key,val),并且把这个pair插入到map中,返回值是一个pair,其中first是一个迭代器,second是一个bool类型。当插入成功,迭代器指向新插入的元素,bool为true,当插入失败,迭代器指向map中原来已有的key和要插入的key相同的键值对,bool位false。

emplace_hint(iter, key, val):作用和emplace相同,不过插入位置由iter指定,而且返回值是迭代器(就是上面的那个first)。对于插入一个元素,使用emplace和emlace_hint效率比insert更高.

key_comp():返回一个用于比较key的比较器

value_comp():返回一个用于比较value的比较器

特殊的迭代器

begin,end,cbegin,cend,rbegin,rend,crbegin,crend