1. 序列式容器与关联式容器的区别
- 序列式容器:其底层为线性序列的数据结构,里面储存的是元素本身。
- Eg:vector,list,string,deque,stack,forward_list(C++11)。
- 关联式容器:其底层为树形序列的数据结构,里面储存的是<key,value>结构的键值对。
- Eg:map,set,multimap,multist。
2. set 和 map 介绍
- set和map是C++标准库中的关联容器,它们中的所有元素都会根据元素的键值(key)自动被排序,又由于红黑树(RB-tree)是一种平衡二叉搜索树,自动排序效果非常好,所以标准的STL中的set和map容器都是以红黑树(RB-tree)为底层机制,又由于map和set所开放的各种操作接口,RB-tree也提供了,所以它们几乎所有的操作行为,都只是转调RB-tree的操作行为。
- set是存储已排序的无重复的元素,它有过滤重复功能,它元素的键值就是实值,实值就是键值(在底层实际存放的是由<value, value>构成的键值对),所以set的迭代器不能改变其元素值,否则会严重破坏其set有序的组织,所以其迭代器被定义为底层RB-tree的const_iterator,杜绝写入操作。
- map中所有元素都会根据元素的键值自动被排序。map 的所有元素都是 pair,同时拥有实值(value)和键值(key)。pair 的第一元素被视为键值,第二元素被视为实值。map不允许两个元素拥有相同的键值。
3. 红黑树特性
- 每个节点要么是黑色,要么是红色。
- 根节点是黑色。
- 每个叶子节点都是黑色。
- 每个红色结点的两个子结点一定都是黑色。
- 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
- 插入的节点默认为红色。
4. Set和Map以及MultiSet和MultiMap
- set和map的联系与区别:
- 底层都是由红黑树实现的(结构都一样);
- 都是按照key排序,并且不允许key重复(通过将变量设置为const来实现);
- set是只有一个key,map是一个pair对,包含key和value;
- 是否有multi的区别:是否允许key重复,相同key的节点肯定在相邻的地方;
5. Set 和 Map 的使用方法
Set
int arr[5] = {5, 2, 3, 4, 1};
set<int> st1;
set<int> st2(arr, arr+5);
set<int> st3(st2);
// greater<int> 是一个模板参数,它表示使用逆序比较函数来对元素进行排序,即以降序排列。
// 因此,这个 set 将按照整数值的降序存储其元素。
set<int, greater<int>> st4;
// 默认情况下,使用正向迭代器遍历set是进行中序遍历,得到一组升序的数据。
set<int> st1(arr, arr + 5);
set<int, greater<int>> st2(arr, arr + 5);
set<int>::iterator it1 = st1.begin();
while(it1 != st1.end()) cout << *it1++ << endl; // 正向迭代器遍历st1,得到一组升序数据
set<int, greater<int>> iterator it2 = st2.begin();
while(it2 != st2.end()) cout << *it2++ << endl; // 正向迭代器遍历st2,得到一组降序数据
st1.insert(5);
st1.erase(0);
st1.erase(st1.begin(), st1.end());
st1.clear();
set<int>::iterator pos1 = st1.find(20); // 查找特定数据
int count = st1.count(20); // 统计set中20出现的次数
int size = st1.size(); // 返回当前set容器中的元素个数 O(1)
set<int>::iterator it1 = st.lower_bound(30); // 返回大于或等于x的最小节点
set<int>::iterator it3 = st.upper_bound(40); // 返回大于x的最小节点
pair<set<int>::iterator, set<int>::iterator> pa = st.equal_range(50); // 返回包含x的区间:pa.first = 50, pa.second = 60
map
map<int, int> mp1;
mp1[1] = 1;
mp1[2] = 3;
map<int, int> mp2(mp1.begin(), mp1.end());
map<int, int> mp3(mp2);
mp4 = mp1; // 赋值
map<int, int>::iterator it = mp.begin();
while (it != mp.end()){
// cout << (*it).first << "=>" << (*it).second << endl;
cout << it->first << "=>" << it->second << endl;
++it;
}
mp.insert(make_pair(5, 5)); // 插入数据
mp.erase(9);
mp.erase(mp.begin()+1);
mp.clear();
map<int, int>::iterator it = mp.find(3);
6. unordered_set 和 unordered_map
- multiset:元素可以重复,且元素有序
- unordered_set:元素无序且只能出现一次
- unordered_multiset:元素无序可以出现多次
6.1 set 和 unordered_set
- unordered_set 是不按特定顺序存储唯一元素的容器,允许基于它们的 key 快速检索单个元素
- 在 unordered_set 中,元素的值与唯一标识它的 key 同时存在,key 是不可变的,因此,unordered_set 中的元素在容器中不能被修改,但是它可以进行插入和删除
- 在内部,unordered_set 中的元素不按任何特定顺序排序,而是根据它们的哈希值插入到相应的桶中,允许直接根据它们的值快速访问单个元素(平均时间复杂度恒定,即常数O(1) )
- Unordered_set 容器在通过键访问单个元素时比 set 容器更快,尽管它们在通过元素子集进行范围迭代时通常效率较低
- 容器中的迭代器至少是前向迭代器(只支持单向迭代器 ++)
6.2 map 和 unordered_map
- 底层实现不同,Map的底层是红黑树,Unordered_Map的底层是哈希表(散列表);
- Map是红黑树实现的,所以有排序功能,通过迭代器就可以输出排好序的序列,Unordered_Map没有排序的功能;
- Map查找和插入的时间复杂度是O(logn),Unordered_Map是接近O(1),跟哈希函数相关,所以不需要排序的时候用Unordered_Map效率会更高;
- Unordered_Map的数据,如果数据数量超过线性存储表的数量查找效率就会降低,线性存储表的容量就要增加一倍(类似于vector),是根据经验得到的,没有严谨的数学证明。