🌇前言
set
和 map
是 STL
中的容器之一,不同于普通容器,它俩的查找速度极快,常用来存储各种经常被检索的数据,因为这俩容器的底层是平衡二叉搜索树中的红黑树。除此之外,还可以借助其特殊的性质,解决部分难题
🏙️正文
1、预备知识
在正式学习 set
和 map
之前,首先要有一些预备知识,否则后面可能看不懂相关操作
1.1、关联式容器
在以往的 STL
容器学习中,我们接触到的都是 序列式容器,比如 string
、vector
、list
、deque
等,序列式容器的特点就是 底层为线性序列的数据结构,就比如 list
,其中的节点是 线性存储 的,一个节点存储一个元素,其中存储的元素都可序,但未必有序
关联式容器 则比较特殊,其中存储的是 的 键值对,这就意味着可以按照 键值大小
key
以某种特定的规则放置于适当的位置,关联式容器 没有首尾的概念,因此没有头插尾插等相关操作,本文中学习的 set
和 map
就属于 关联式容器
出自《STL源码剖析》
注意:stack
、queue
等适配器也属于序列式容器,因为他们的底层是 deque
等容器
1.2、键值对
键值对是 一种用来表示具有一一对应关系的结构,该结构中一般只包含两个成员变量:key
和 value
,前者表示 键值,后者表示 实值
关联式容器的实现离不开键值对
因此在标准库中,专门提供了这种结构 pair
定义如下
//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) {} #ifdef __STL_MEMBER_TEMPLATES template <class U1, class U2> pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {} #endif };
pair
中的 first
表示 键值,second
则表示 实值,在给 关联式容器 中插入数据时,可以构建 pair
对象
比如下面就构建了一个 键值 key
为 string
,实值 value
为 int
的匿名 键值对 pair
对象
pair<string, int>("hehe", 123); • 1
可以将此匿名对象传入 关联式容器 中,当然这样写未免过于麻烦了,于是库中设计了一个函数模板 make_pair
,可以根据传入的参数,去调用 pair
构建对象并返回
make_pair("hehe", 123); //构建出的匿名对象与上面的一致 • 1
make_pair
的定义如下所示:
template <class T1,class T2> pair<T1,T2> make_pair (T1 x, T2 y) { return ( pair<T1,T2>(x,y) ); }
该函数实际会被编译器优化为 内联函数,因此不会造成过多消耗,可以放心使用
1.3、树型结构的关联式容器
所以在 C++
标准中,共提供了四种 树型结构的关联式容器
set
multiset
map
multimap
关于 哈希结构的关联式容器 将在 哈希表 中学习
树型结构与哈希结构的关联式容器功能都是一模一样的,不过 哈希结构查找比树型结构快得多 -> O(1)
注:
STL
中选择的树型结构为 红黑树RB-Tree
- 树型结构中的元素 中序遍历 后有序,而哈希结构中的元素无序
2、set
2.1、什么是 set?
set
其实就是之前在 二叉搜索树 中key
的模型
set
只包含 实值 value
,或者说它的 实值就是键值,键值就是实值
其中的 T
就是 set
的实值(键值),参数2 Compare
为存储依据,默认为升序,即符合 二叉搜索树 中序遍历的结果:升序,参数3 Alloc
是空间配置器,现在不必深究
作为 STL
中的容器,set
当然少不了迭代器,树型关联式容器迭代器的遍历结果为有序,所以迭代器遍历的本质是 中序遍历,同时 set
的迭代器还是一个 双向迭代器,支持 ++
和 --
操作
下面来看看 set
的相关操作
2.2、set 的使用
set
的构造函数如下图所示:
可以直接创建一个空 set
使用,也可以根据迭代器区间创建 set
注意:创建时需要指定实值的类型
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 8,5,6,7,3,1,1,3 }; set<int> s1; //创建一个空的 set set<int> s2(arr.begin(), arr.end()); //创建包含数据的 set cout << "s1: "; for (auto e : s1) cout << e << " "; cout << endl; cout << "s2: "; for (auto e : s2) cout << e << " "; cout << endl; return 0; }
就像 二叉搜索树 一样,set
是不支持数据冗余的,如果出现冗余的数据插入时,会失败,如果想存储冗余的数据,可以使用 multiset
set
中的常用功能
功能 | 用途 |
迭代器 | 遍历容器 |
empty |
判断容器是否为空 |
size |
当前容器中的元素数 |
max_size |
容器的最大容量 |
insert |
元素插入,根据特定条件插入至合适位置 |
erase |
删除指定元素 |
swap |
交换两个容器 |
clear |
清空容器中的所有元素 |
find |
查找实值是否存在并返回迭代器位置 |
count |
统计容器中指定键值的数量 |
下面这段代码演示了上述功能的实际效果
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 7,3,6,9,3,1,6,2 }; set<int> s1(arr.begin(), arr.end()); //迭代器遍历 cout << "迭代器遍历结果: "; set<int>::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << " "; ++it; } cout << endl; //判空、求大小 cout << "===================" << endl; cout << "empty(): " << s1.empty() << endl; cout << "size(): " << s1.size() << endl; cout << "max_size(): " << s1.max_size() << endl; //插入元素 cout << "===================" << endl; cout << "insert(5): "; s1.insert(5); for (auto e : s1) cout << e << " "; cout << endl; //删除元素 cout << "===================" << endl; cout << "erase(6): "; s1.erase(6); for (auto e : s1) cout << e << " "; cout << endl; //交换、查找、清理 cout << "===================" << endl; set<int> s2(arr.begin() + 5, arr.end()); s1.swap(s2); cout << "s1: "; for (auto e : s1) cout << e << " "; cout << endl; cout << "s2: "; for (auto e : s2) cout << e << " "; cout << endl; cout << "s1.find(9): "; cout << (s1.find(9) != s1.end()) << endl; cout << "s2.clear(): " << endl; s2.clear(); cout << "s1: "; for (auto e : s1) cout << e << " "; cout << endl; cout << "s2: "; for (auto e : s2) cout << e << " "; cout << endl; return 0; }
至于 count
也可以用来查找元素是否存在,对于 set
来说,键值 key
就是 实值 value
,并且因为不允许冗余,所以只有一个 键值,count
统计 键值 数量不就相当于 查找 吗?
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 7,3,6,9,3,1,6,2 }; set<int> s1(arr.begin(), arr.end()); for (int i = 0; i < 10; i++) { if (s1.count(i)) cout << i << " 在 set 中" << endl; else cout << i << " 不在 set 中" << endl; } return 0; }
可以通过改变 set
模板参数2的方式,改变其中的顺序为 降序
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 7,3,6,9,3,1,6,2 }; set<int, greater<int>> s1(arr.begin(), arr.end()); for (auto e : s1) cout << e << " "; return 0; }
注意:键值 key
是不允许改变的,如果改变了,会破坏二叉搜索树的原则,因此即使是 set
中的普通迭代器,本质上也是 const
迭代器,非常神奇
2.3、set 的特点
set
具有以下特点:
set
还有一个亲兄弟:multiset
,它允许数据冗余,即数据插入一定是成功的
2.4、multiset
multiset
是 set
的另一个版本,对于 multiset
来说,插入冗余数据时,并不会失败
除此之外,multiset
和 set
的操作没什么区别,一模一样
这里就不再赘述,而是单独演示一下允许数据冗余的效果
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 3,5,3,4,5,9,2,3 }; multiset<int> ms1(arr.begin(), arr.end()); for (auto e : ms1) cout << e << " "; cout << endl; return 0; }
值得一提的是,当在 multiset
中查找冗余的数据时,返回的是 中序遍历中,第一次出现的元素
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 3,5,3,4,5,9,2,3 }; multiset<int> ms1(arr.begin(), arr.end()); auto it = ms1.begin(); while (it != ms1.end()) { cout << *it << " | " << &*(it) << endl; ++it; } cout << "================" << endl; cout << "ms1.find(3): " << &*(ms1.find(3)) << endl; return 0; }
所以,multiset
才是真正的排序,set
则是去重 + 排序
统计 键值 数 count
在 multiset
中可以发挥真正效果
#include <iostream> #include <vector> #include <set> using namespace std; int main() { vector<int> arr = { 3,5,3,4,5,9,2,3 }; multiset<int> ms1(arr.begin(), arr.end()); for (int i = 0; i < 10; i++) cout << i << "在 multiset 中的数量: " << ms1.count(i) << endl; return 0; }
在实际中,multiset
用的比较少,重点掌握 set
即可
3、map
3.1、什么是 map?
map
是 二叉搜索树 改造后的 key / value
模型,是一个真正意义上的 键值对,应用场景如下:
map
的定义如下
其中包含两个模板参数:
Key
就是键值对中的 键值T
则是键值对中的 实值
在 map
中会用到前面提到过的 pair
结构,其中 first
表示键值,second
表示实值
map
也有迭代器,也是 双向迭代器
3.2、map 的使用
构造 map
有以下几种方法
#include <iostream> #include <vector> #include <map> using namespace std; int main() { vector<pair<string, int>> arr = { make_pair("G", 71), make_pair("A", 65), make_pair("F", 70) }; map<string, int> m1; //创建一个空的 map map<string, int> m2(arr.begin(), arr.end()); //创建包含数据的 map cout << "m1: " << endl; for (auto e : m1) cout << e.first << " | " << e.second << endl; cout << "========================" << endl; cout << "m2: " << endl; for (auto e : m2) cout << e.first << " | " << e.second << endl; return 0; }
注意:在访问 map
中的 键值 和 实值 时,需要通过 pair
对象指定访问,比如 e.first
map
中的常用功能
功能 | 用途 |
迭代器 | 遍历容器 |
empty |
判断容器是否为空 |
size |
当前容器中的元素数 |
max_size |
容器的最大容量 |
operator[] |
按照键值,访问实值,如果没有,则新插入 |
insert |
元素插入,根据特定条件插入至合适位置 |
erase |
删除指定元素 |
swap |
交换两个容器 |
clear |
清空容器中的所有元素 |
find |
查找实值是否存在并返回迭代器位置 |
count |
统计容器中指定键值的数量 |
除了新增了一个 operator[]
以及部分函数返回值不一样外,与 set
没啥区别
#include <iostream> #include <vector> #include <string> #include <map> using namespace std; int main() { vector<pair<string, int>> arr{make_pair("z", 122), make_pair("a", 97), make_pair("K", 75), make_pair("h", 104), make_pair("B", 66)}; map<string, int> m1(arr.begin(), arr.end()); //迭代器遍历 cout << "迭代器遍历结果: "; map<string, int>::iterator it = m1.begin(); while (it != m1.end()) { cout << "<" << it->first << ":" << it->second << "> "; ++it; } cout << endl; //判空、求大小、解引用 cout << "===================" << endl; cout << "empty(): " << m1.empty() << endl; cout << "size(): " << m1.size() << endl; cout << "max_size(): " << m1.max_size() << endl; cout << "m1[""a""]: " << m1["a"] << endl; //插入元素 cout << "===================" << endl; cout << "insert(""a"", 5): "; m1.insert(make_pair("a", 5)); for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; //删除元素 cout << "===================" << endl; cout << "erase(""a""): "; m1.erase("a"); for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; //交换、查找、清理 cout << "===================" << endl; map<string, int> m2(arr.begin() + 2, arr.end()); m1.swap(m2); cout << "m1.swap(m2)" << endl; cout << "m1: "; for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; cout << "m2: "; for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; cout << "m1.find(""B""): "; cout << (m1.find("B") != m1.end()) << endl; cout << "m2.clear()" << endl; m2.clear(); cout << "m1: "; for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; cout << "m2: " << endl; for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> "; cout << endl; return 0; }
同样的,map
不允许数据冗余,如果想插入重复的数据,可以使用 multimap
map
插入的返回值比 set
略微复杂,因为 既要表示是否成功,也要返回插入成功的迭代器,所以返回值是一个 pair
#include <iostream> #include <vector> #include <string> #include <map> using namespace std; int main() { map<string, int> m1; auto ret = m1.insert(make_pair("a", 97)); cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " | " << ret.second << endl; ret = m1.insert(make_pair("a", 100)); cout << "<" << ret.first->first << ":" << ret.first->second << ">" << " | " << ret.second << endl; return 0; }
至于 find
和 count
跟 set
中的一样,可以用来判断元素是否存在,不过 find
返回的是 迭代器,count
返回的则是 键值数
map
是支持修改 实值 value
的,因此 可以根据普通迭代器修改 实值
#include <iostream> #include <vector> #include <string> #include <map> using namespace std; int main() { map<string, int> m1; m1.insert(make_pair("a", 97)); auto it = m1.find("a"); cout << "<" << it->first << ":" << it->second << ">" << endl; it->second = 668; cout << "<" << it->first << ":" << it->second << ">" << endl; return 0; }
使用 map
来实现水果统计的代码
#include <iostream> #include <vector> #include <string> #include <map> using namespace std; int main() { vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" }; map<string, int> table; for (auto& e : word) { auto ret = table.find(e); if (ret == table.end()) table.insert(make_pair(e, 1)); else ret->second++; } for (auto e : table) cout << "<" << e.first << ":" << e.second << ">" << endl; return 0; }
可以实现统计,但这种写法太麻烦了,实际不会这么写,可以使用 operator[]
实现更高级的写法
3.3、map 中的 operator[]
operator[]
返回的是当前 键值 对应的 实值,如果当前 键值 不存在,则会插入新的 键值对
借助此特性,可把代码优化为
#include <iostream> #include <vector> #include <string> #include <map> using namespace std; int main() { vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" }; map<string, int> table; for (auto& e : word) table[e]++; for (auto e : table) cout << "<" << e.first << ":" << e.second << ">" << endl; return 0; }
显然,map
中的 operator[]
是一个非常强大的功能
operator[]
的返回值为 mapped_type
,即 实值value
的引用,参数 key_type
是 键值key
重点在于 operator[]
的实现:如何凭借 键值 返回对应的 实值,并且做到新键值对的插入
(*((this->insert(make_pair(k, mapped_type()))).first)).second • 1
总的来说,operator[]
返回时需要经历以下步骤:
- 插入一个新的键值对
this->insert( make_pair(k, mapped_type()) )
- 获取
insert
返回值中的 键值返回值.first
即迭代器iterator
- 最后通过迭代器获取 实值
(*iterator).second
只需三步,即可获取 实值
其实上面那样定义还复杂了,可以优化为下面这个样子
( (this->insert( make_pair(k, mapped_type()) )).first )->second
所以一个 operator[]
兼顾了这几种功能:插入、修改、插入+修改、查找
是 map
中最强大的功能
3.4、map 的特点
归纳总结后,map
的特点如下图所示
注意:无论是查找、插入、删除还是排序,都只看 键值 key
,至于 实值 value
的内容是什么,无所谓,它只不过是 键值 额外携带的一个信息包而已
multimap
允许出现键值冗余
3.5、multimap
multimap
中允许出现多个 重复的键值,这就意味着 operator[]
无法确认调用者的意图 -> 不知道要返回哪个 键值 对应的 实质
所以 multimap
中没有提供 operator[]
除了 允许键值冗余 和 没有 operator[]
这个两个特点外,multimap
和 map
在操作上没有区别
当然,查找 find
时,返回的是中序遍历中第一次出现元素的迭代器;计数 count
返回的则是当前 键值 的数量
与 multiset
一样,multimap
用的也比较少,重点掌握 set
和 map
即可
4、相关试题实战
学会使用 set
和 map
后,可以将其用于实战,比如在下面这两个题中,这两个容器可以让我们事半功倍
4.1、前K个高频单词
题目链接:692. 前K个高频单词
题目分析:题目很短,就是在一个字符串数组中,找出前 k
个出现频率最高的单词
注意:如果出现次数相同,则按字典序排序
这道题有很多种解法
解法一:map
+ 快排
利用 map
建立 的映射关系,在按照字典序排序的同时统计出每个单词的出现频率,再通过快排依照数量进行二次排序,选择前
k
个高频单词即可
因为基础版快排 不稳定,可能会导致频率相同的单词顺序出问题,即违背题目要求:如果出现频率相同,则按字典序排序
所以这里需要使用 稳定版快排 stable_sort
,如果频率相同,保持原有顺序
//map + stable_sort class Solution { public: struct Compare { bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const { return kv1.second > kv2.second; } }; vector<string> topKFrequent(vector<string>& words, int k) { //统计每个单词出现的频率,同时先按照字典序排序 map<string, int> table; for(auto e : words) table[e]++; //将处理好的数据存入数组中 vector<pair<string, int>> vTable(table.begin(), table.end()); //按照出现频率进行二次排序 stable_sort(vTable.begin(), vTable.end(), Compare()); //取出前 k 个高频单词 vector<string> vs; for(int i = 0; i < k; i++) vs.push_back(vTable[i].first); return vs; } };
注意:此时使用快排进行排序时,单个元素是 pair
,需要自己写出仿函数进行排序,仿函数十分强大
难道基础版快排无法完成任务吗?
当然可以,只需要将 仿函数进行设计即可:优先按照出现频率排序,如果频率相同,则按照字典序排序即可
具体代码如下(用了一点 C++11
中的知识)
//map + sort class Solution { public: vector<string> topKFrequent(vector<string>& words, int k) { //统计每个单词出现的频率,同时先按照字典序排序 map<string, int> table; for(auto e : words) table[e]++; //将处理好的数据存入数组中 vector<pair<string, int>> vTable(table.begin(), table.end()); //按照出现频率进行二次排序 sort(vTable.begin(), vTable.end(), [](const pair<string, int>& kv1, const pair<string, int>& kv2)->bool { return kv1.second == kv2.second ? kv1.first < kv2.first : kv1.second > kv2.second; }); //取出前 k 个高频单词 vector<string> vs; for(int i = 0; i < k; i++) vs.push_back(vTable[i].first); return vs; } };
C++11
中的 lambda
表达式还是很香的
注意: 优先按照出现频率进行排序,如果频率相同时,就按字典序排序,所以写成 kv1.first < kv2.first
(小的单词排在前面,就是字典序)
解法二:map
+ set
同样的,先使用 map
统计单词出现频率,此时已经按照字典序进行了排序,然后将 pair
看作一个 键值 存入 set
中,改变 set
中的比较逻辑(先按出现频率排序,如果相关就按照字典序排序)
整体思路与 map
+ sort
没啥区别,不过此时是直接使用 set
进行排序,没必要借助 vector
//map + set class Solution { public: struct Compare { bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const { return kv1.second == kv2.second ? kv1.first < kv2.first : //如果两个频率相等,比较字典序 kv1.second > kv2.second ; //不相等比较频率 } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string, int> table; for(auto e : words) table[e]++; set<pair<string, int>, Compare> sortSet(table.begin(), table.end()); vector<string> vs; auto it = sortSet.begin(); for(int i = 0; i < k; ++it, ++i) vs.push_back(it->first); return vs; } };
解法三:map
+ multimap
这个解法就有点狠了,直接使用 map
与 multimap
互导,完成排序
map
按照字典序排序,并统计出频率
multimap
在 map
的基础上,按照 频率 排序
注意: 需要使用 multimap
,避免相同频率的单词丢失
//map + multimap class Solution { public: vector<string> topKFrequent(vector<string>& words, int k) { //先统计出现频率,同时按照字典序排序 map<string, int> mapTbale; for(auto e : words) mapTbale[e]++; //将 map 中的数据存入 multimap 中,按照频率排序 multimap<int, string, greater<int>> multimapTable; for(auto &e : mapTbale) multimapTable.insert(make_pair(e.second, e.first)); //取出前k个高频单词 vector<string> vs; auto it = multimapTable.begin(); for(int i = 0; i < k; ++it, ++i) vs.push_back(it->second); return vs; } }; • 23
这种写法十分巧妙,代码也很简洁,完美体现了 map
和 multimap
的价值
关于这道题还有其他解法,比如 利用优先级队列解决 Tok-K
,感兴趣的同学可以自己下去研究,这里就不再展开叙述
4.2、复杂链表的复制
题目链接:剑指 Offer 35. 复杂链表的复制
题目分析:复杂链表的深度拷贝,将题目给定的链表进行复制,这个链表比较特殊,不仅指向下一个节点,还随机指向空或其他节点
之前的解法是在两个节点新增节点,然后更改链接关系,比较麻烦,现在可以借助 map
建立映射关系,直接照着原链表更改链接关系即可
//剑指 Offer 35. 复杂链表的复制 //https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/ class Solution { public: Node* copyRandomList(Node* head) { map<Node*, Node*> copyNodeMap; //存放原来的链表节点,及新的链表节点 Node* cur = head; Node* copyHead = nullptr; Node* copyTail = nullptr; //先拷贝出链表 while(cur) { Node* copy = new Node(cur->val); copyNodeMap[cur] = copy; if(copyHead == nullptr) { copyHead = copyTail = copy; } else { copyTail->next = copy; copyTail = copyTail->next; } cur = cur->next; } //初步拷贝已完成,进行随机指针的拷贝 cur = head; while(cur) { //非常重要的一步 copyNodeMap[cur]->random = copyNodeMap[cur->random]; cur = cur->next; } return copyHead; } };
map
在这种场景中是非常强大的!使得 原链表节点和新链表节点之间形成了一种羁绊关系,但 两者之间互不影响
5、补充:交集与差集
下面是一些补充知识,主要是关于 交集和差集 的
5.1、如何查找交集?
交集,指两个数组中相同的元素所构成的集合
求交集的步骤如下:
- 先将两个数组 排序 + 去重
- 遍历两个数组
- 如果不相等,小的
++
- 相等就是交集,记录下来
- 其中一方走完,所有交集就查找完了
排序 + 去重,这就不就是 set
吗?
题目链接:349. 两个数组的交集
直接上代码
//349. 两个数组的交集 //https://leetcode.cn/problems/intersection-of-two-arrays/description/ class Solution { public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { //排序 + 去重 set<int> s1(nums1.begin(), nums1.end()); set<int> s2(nums2.begin(), nums2.end()); //查找交集 vector<int> v; auto it1 = s1.begin(); auto it2 = s2.begin(); while(it1 != s1.end() && it2 != s2.end()) { if(*it1 < *it2) ++it1; else if(*it1 > *it2) ++it2; else { v.push_back(*it1); ++it1; ++it2; } } return v; } };
5.2、如何查找差集?
至于差集的查找,思路和交集差不多
求差集的步骤如下:
- 先将两个数组 排序 + 去重
- 遍历两个数组
- 如果相等,同时
++
- 不相等,小的一方记录后,再
++
- 其中一方走完,再遍历另一方,此时其中的所有元素都是差集
🌆总结
以上就是本次关于 C++【set 和 map 学习和使用】的全部内容了,在这篇文章中我们先学习了 关联式容器相关知识,然后学习了 set
、multiset
、map
以及 multimap
的使用,最后通过一些题目见识到了 set
和 map
的强大之处,希望你在阅读本文后,能够收获相关知识
相关文章推荐
C++ 进阶知识
C++【二叉搜索树】
C++【多态】
C++【继承】
STL 之 泛型思想
C++【模板进阶】
C++【模板初阶】