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