跳到主要内容C++ set 与 map 容器使用详解 | 极客日志C++算法
C++ set 与 map 容器使用详解
C++ STL 中 set 和 map 基于红黑树实现有序存储。set 用于去重和排序,不支持修改元素值,提供 lower_bound、upper_bound 查找边界。map 存储键值对 pair,key 不可变,value 可变。通过 insert 插入数据,支持 count、find 等操作。operator[] 在 map 中若 key 不存在则自动插入默认值并返回引用,常用于词频统计。multiset/multimap 允许重复键。
清酒独酌1 浏览 一。set
底层是搜索树中的红黑树,key 模型
查找成功,返回节点的迭代器;查找失败返回 end()
返回 key 这个值出现了几次,由于所有元素唯一,有就返回 1,没有就返回 0
#include<iostream>
#include<set>
using namespace std;
void test_set1() {
set<int> s;
s.insert(3); s.insert(2); s.insert(4); s.insert(5); s.insert(1); s.insert(5); s.insert(2); s.insert();
set<>::iterator it = s.();
(it != s.()) {
cout << *it << ;
++it;
}
cout << endl;
( e : s) {
cout << e << ;
}
cout << endl;
(s.() != s.()) {
cout << << endl;
}
(s.()) {
cout << << endl;
}
pos = s.();
(pos != s.()) s.(pos);
s.();
( e : s) {
cout << e << ;
}
cout << endl;
}
5
int
begin
while
end
" "
for
auto
" "
if
find
5
end
"找到了"
if
count
5
"找到了"
auto
find
3
if
end
erase
erase
30
for
auto
" "
第 17 行,set 不允许修改:iterator 和 const_iterator 都是 const_iterator
typedef typename rep_type::const_iterator iterator;
typedef typename rep_type::const_iterator const_iterator;
第 30 行是 set 自己的 find
第 31 行是算法库里的 find,用迭代器区间遍例,暴力查找,效率低下
找 >= 的(找下界)
找 > 的(找上界)
void test_set1() {
std::set<int> myset;
std::set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++) myset.insert(i * 10);
itlow = myset.lower_bound(30);
itup = myset.upper_bound(60);
cout << *itlow << endl;
cout << *itup << endl;
myset.erase(itlow, itup);
for (auto e : myset) {
cout << e << " ";
}
cout << endl;
}
找相等值的 [ ) 区间
count 和 equal_range 是专门为 multiset 设计的
void test_set2() {
multiset<int> s;
s.insert(3); s.insert(5); s.insert(8); s.insert(7); s.insert(7); s.insert(9); s.insert(7);
for (auto e : s) {
cout << e << " ";
}
cout << endl;
auto pos = s.find(7);
while (pos != s.end()) {
cout << *pos << " ";
++pos;
}
cout << endl;
cout << s.count(7) << endl;
auto ret = s.equal_range(7);
auto itlow = ret.first;
auto itup = ret.second;
cout << *itlow << endl;
cout << *itup << endl;
s.erase(itlow, itup);
for (auto e : s) {
cout << e << " ";
}
cout << endl;
}
二。map
不能修改 key,可以修改 value
pair 是类模板的结构,map 节点里存数据使用 pair 存的
key 是任意类型都可以,树里面要进行比较大小
如果该类型支持比较大小最好;不支持我们可以自己写仿函数控制
namespace key_value {
template<class K, class V> struct BSTreeNode {
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
pair<K, V> _kv;
BSTreeNode(const K& key, const V& value) :_left(nullptr) , _right(nullptr) , _key(key) , _value(value) { }
};
pair<K, V>& operator*() { return _node->_kv; }
pair<K, V>* operator->() { return &_node->_kv; }
}
迭代器最终要重载 operator*,对迭代器解引用,迭代器里面包含节点的指针;C++ 不支持返回俩值
如果是 _key 和 _value,无法返回
要返回多个值,得返回结构
插入的是一个结构对象
5-7 行:调构造函数,构造一个对象
不用把 key 写成 const,自己内部会转换
第 10 行:类 pair 提供了函数模板 make_pair,等价于第 7 行的写法:自动调 pair 的构造
没有效率问题,会被定义为 inline
重点掌握 10.13 行
void test_map1() {
map<string, string> dict;
pair<string, string> kv1("insert", "插入");
dict.insert(kv1);
dict.insert(pair<string, string> ("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert({ "string", "字符串" });
string str1 = "hello";
A aa1 = { 1, 2 };
pair<string, string> kv2 = { "string", "字符串" };
}
class A
{
public:
A(int a1, int a2) :_a1(a1) , _a2(a2) { }
private:
int _a1;
int _a2;
};
void test_map2() {
map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));
dict.insert(make_pair("insert", "xxxx"));
auto it = dict.begin();
while (it != dict.end()) {
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
for (const auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
}
库里没有重载 pair 这个类的流插入、留提取
*it 返回的是 pair
只要 T(里面的数据) 是结构时,要用迭代器重载的->。恰好 map 里面的数据是结构
operator[ ]
void test_map3() {
string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto e : arr) {
countMap[e]++;
}
for (const auto& kv : countMap) {
cout << kv.first << ":" << kv.second << endl;
}
}
以前 string vector deque 的 [ ] 都是用下标,直接返回第 i 个数据的引用(可读可写)
map 的 [ ] 不常规,给 key 返回对应 value 的引用
上面的例子 countMap[e]++,如果 countMap 里面有对应水果,出现一次就++
那第一次是怎么实现的呢?库里面给了
[ ] 通过 insert 实现,借助了 insert 的返回值
1. key 已经在树里面:返回 pair<树里面 key 所在节点的 iterator,false>
2. key 不在树里:返回 pair<新插入 key 所在节点的 iterator,true>
可以看出,insert 还有查找的价值
V& operator[](const K& key) {
pair<iterator, bool> ret = insert(make(key, V()));
return ret.first->second;
}
若 key 没在树里,调 V 的默认构造,插入成功返回 pair 类型的 ret,拿到 key 迭代器,通过迭代器取到 value 的引用,并返回
第一次:[ ] 是插入,++是修改返回值
若 key 在树里,插入失败返回 pair 类型的 ret,拿到 key 迭代器,通过迭代器取到 value 的引用,并返回
void test_map4() {
map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));
cout << dict["sort"] << endl;
dict["map"];
dict["map"] = "映射,地图";
dict["insert"] = "xxx";
dict["set"] = "集合";
for (const auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
}
nultimap 没有提供 [ ],不知道映射哪一个
insert 也不一样,multmap 插入永远成功
哈希用迭代器走不有序,有序是搜索树特有的
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online