C++的map介绍
关联式容器
在数据结构的初级阶段, 我们使用了如: vector, list, deque这样的序列式容器. 之所以为序列式容器, 是因为他们的底层是线性序列的数据结构, 如我们讲到的deque, 底层就是分段的连续线性结构.
那么说明时候关联式容器呢? 关联式同样也是存储数据的, 只不过他不是存放单一的数据, 而是存放的键值对. 这个和Python的字典是一样的. 这样的容器通常在数据检索的时候效率很高, 比序列式容器的检索快很多.
键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。
SGI-STL中关于键值对的定义:template <class T1, class T2> struct pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; pair(): first(T1()), second(T2()) {} pair(const T1& a, const T2& b): first(a), second(b) {} };
树形结构的关联式容器
根据应用场景的不同, STL总共实现了两种不同结构的管理式容器: 树形结构和哈希结构. 树形结构主要有4种: map, set, multimap, multiset. 他们的共同点是: 底层都使用的是平衡搜索二叉树(红黑树), 容器内的元素都是一个有序的序列, 该篇博客主要讲解map.
快速熟悉map
map的概念
从文档中我们不难看出以下六点:
map是关联容器, 它按照特点的次序(按照key来进行比较)存储由键key和值value组成的键值对.
在map内, key用于排序和惟一确定一个键值对, 而value则用于存放和key相关的内容. key和value的类型通常是不一样的. 在map内部, key和value通过成员类型value_type绑定, 取别名为pair.
typedef pair<const key, T> value_type;
在map内, 总是按照key来进行排序的.
map通过键值对来访问随机元素比unordered_map要慢, 但是map可以直接顺序迭代, 得到一个有序序列.
map支持下标访问, 通过[key]来得到元素. 并且和Python的字典一样, 他可以通过下标来新增元素.
map的底层为平衡搜索二叉树(红黑树).
map的基本使用
#include <iostream> #include <string> #include <set> #include <map> int main() { std::map<std::string, int> map; // pair的使用不需要额外引头文件 map.insert(std::pair<std::string, int>("pangchao", 123)); map.insert(std::pair<std::string, int>("liqing", 456)); map.insert(std::pair<std::string, int>("heqing", 789)); map["wanger"] = 159; std::map<std::string, int>::iterator it = map.begin(); while(it != map.end()) { std::cout << it->first << ": " << it->second << std::endl; ++it; } std::cout << "size: " << map.size() << std::endl; std::cout << "empty: " << map.empty() << std::endl; return 0; }
运行结果如下, 可以看到map的使用和STL的其他容器使用起来非常像, 几个接口的名字都是一样的, 这得益于这种统一的思想.
[pangchao@VM-4-9-centos 230403]$ make clean;make rm -f test g++ test.cc -o test -std=c++11 [pangchao@VM-4-9-centos 230403]$ ./test heqing: 789 liqing: 456 pangchao: 123 wanger: 159 size: 4 empty: 0
两道OJ题
接下来通过两道OJ题目来强化对map的了解.
这里需要特别注意的是, 不可以直接对map进行排序, 原因是map没有重载"-"这个运算符, 我们只能借助中间者来进行排序, 如: vector.
class Solution { public: vector<string> topKFrequent(vector<string>& words, int k) { unordered_map<string, int> dict; for (string& word : words) { if (dict.end() != dict.find(word)) { // 存在于dict中 ++dict[word]; } else { dict[word] = 1; } } // sort(dict.begin(), dict.end()); // 没法对map进行直接排序的 只能借助vector vector<pair<string, int>> result(dict.begin(), dict.end()); sort(result.begin(), result.end(), [](const pair<string, int>& a, const pair<string, int>& b) { if (a.second != b.second) return (a.second) > (b.second); else return (a.first) < (b.first); }); vector<string> tmp; vector<pair<string, int>>::iterator it = result.begin(); while (k != 0 && it != result.end()) { tmp.push_back(it->first); --k; ++it; } return tmp; } };
#include <iostream> #include <string> #include <algorithm> #include <vector> #include <set> #include <map> #include <unordered_map> int main() { std::string source; std::getline(std::cin,source); // 1. 转化为小写 并且进行切分 size_t left = 0; size_t right = 0; std::vector<std::string> tmp; while(right != source.length()) { if(source[right] < 91 && source[right] > 64) { source[right] = source[right] + 32; } if(source[right] == ' ' || source[right] == '.') { tmp.push_back(source.substr(left, right - left)); left = right + 1; } ++right; } // 2. 使用map进行排序 std::map<std::string, int> dict; for (std::string& word : tmp) { if (dict.end() != dict.find(word)) { // 存在于dict中 ++dict[word]; } else { dict[word] = 1; } } // sort(dict.begin(), dict.end()); // 没法对map进行直接排序的 只能借助vector std::vector<std::pair<std::string, int>> result(dict.begin(), dict.end()); std::sort(result.begin(), result.end(), [](const std::pair<std::string, int>& a, const std::pair<std::string, int>& b) { if (a.second != b.second) return (a.second) > (b.second); else return (a.first) < (b.first); }); // 3. 打印 for(const auto& item : result) { std::cout << item.first << ":" << item.second << std::endl; } return 0; }