详情见:https://zcheng.ren/2016/09/09/STLSetAndMap/
今天的内容需要用到红黑树的知识,因为set和map都是通过RBTree实现的,因此,推荐大家先看这两篇博客,理解红黑树的性质:
一、set模块:
- set以红黑树作为其底层容器,实现set其实就在调用红黑树的接口而已;
- 所有元素都会被自动排序,因为RBTree是一种加限定使其平衡的一种二叉搜素树;
- 键值不许重复,不能出现重复元素,set中键就是值啦;
- 通过迭代器无法改变set的值,因为set的值就是键,如果需要修改,则需要删除对应的键节点,然后调整set的结构,然后插入节点之后又需要调整set的结构,这样可能(大部分情况)会导致迭代器失效,所以set中不许修改set中的值;
- set中的迭代器是const的,不允许改变;
6. set的迭代器一旦被赋值,便不会改变了,容易出现问题。
下面我们来看第5个特性的问题实例,不同情况:
1)最大的元素之后设置为set的begin():
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <string>
using namespace std;
int main(){
set<int> s;
s.insert(100);
set<int>::iterator iter = s.begin();
s.insert(20);
s.insert(80);
while (iter != s.end()){
cout << *(iter++) << endl;
}
return 0;
}
运行结果:
2)中间的元素之后设置为set的begin():
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <string>
using namespace std;
int main(){
set<int> s;
s.insert(80);
set<int>::iterator iter = s.begin();
s.insert(20);
s.insert(100);
while (iter != s.end()){
cout << *(iter++) << endl;
}
return 0;
}
运行结果:
3)最小的元素之后设置为set的begin():
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <string>
using namespace std;
int main(){
set<int> s;
s.insert(20);
set<int>::iterator iter = s.begin();
s.insert(80);
s.insert(100);
while (iter != s.end()){
cout << *(iter++) << endl;
}
return 0;
}
运行结果:
结果分析:set是通过RBTree实现的,然而由于set的iter在赋值之后就不会变了,而新插入元素之后会调整RBTree的结构,导致起先设置的begin iterator不是实际中该有的,然后其就损失了一些部分,导致上述情况发生。
multiset模块:
multiset相较set来说,只是键值重复而已,multiset和set的insert函数调用了RBTree的不同的函数接口,multiset调用insert_euqal,set调用insert_unique,因此不同啦!
二、map模块
1)map中不允许键重复,值无所谓的;
2)所有元素是通过键值对方式存在的;
3)map中的所有元素都是排序过的,通过键排序哈;
4)map中的键不许修改,因为map中的RBTree的结构是通过键实现的,和set中不允许修改值的原因一样的,因此无法修改的,但其中的值可是想怎样都可以怎样的啦;
5)map是通过RBTree(红黑树)的结构实现的。
map的实现结构(简易版):
现在我们证明一下map的第3个特性:
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <string>
using namespace std;
int main(){
map<int, string> m;
m.insert(map<int, string>::value_type(20,"hello"));
m.insert(map<int, string>::value_type(9, "yello"));
m.insert(map<int, string>::value_type(30, "xello"));
map<int, string>::iterator iterM=m.begin();
while (iterM!=m.end()){
cout << iterM->first << " " << iterM->second << endl;
iterM++;
}
cout << endl;
return 0;
}
运行结果:
multimap模块
multimap允许插入的键相同,因为multimap和map的insert函数调用的是RBTree的不同的insert接口,multimap调用insert_equal(),而map调用insert_unique(),其他与map相同!
注意:
multiset和multimap都允许在RBTree中插入相同的元素,所以它们的RBTree不是二叉(排序)搜索树!