在完成了STL与泛型编程前两周的学习之后,有一些总结和心得在这里通过学习笔记的方式分享出来,笔记我是跟着老师在视频中所讲的内容按照顺序记录的,也不能说是流水账,对课程中的一些问题还是添加了自己的理解和分析,供也在学习C++的小伙伴用作学习交流,如有理解不到位的地方,欢迎批评指正。
上周,老师就分配器和部分容器结构做了详细的介绍,本周接着上周的内容继续学习了容器的结构与分类。
一.容器deque
deque是双向开口,双向进出的容器
Deque有n个buffer缓冲区,一个map相当于其控制中心,map用一个vector来存放指针,分别指向n个缓冲区。
Deque其实是分段连续的,deque如何模拟连续空间,这全都是deque iterators的功劳,其iterators是一个class,iterators用4根指针,分别指向当前位置、起始位置、终止位置和map控制中心的某个位置,以此来判断是哪个缓冲区。
二.容器queue和stack
容器queue是先进先出的容器;容器stack是一端开口,先进后出的容器;
它们可以选择list和deque作为底层容器去支持它们的运行,标准库默认用deque来作为底层容器。Stack和queue都不允许遍历,也不提供iterator。Stack可以选择vector作为底层容器,但是queue不可以选择vector,原因是queue可以从前端出,即pop_front,但vector没有pop_front.当然,stack和queue都不可以选择set或map作为底层容器,这点很好理解。
三.容器rb_tree
Red-Black tree(红黑树)是平衡二元搜索树(balanced binary search tree)中常被使用的一种。其特征是:排列规则有理搜索和安插,并保持适度平衡,即无任何节点过深。
Rb_tree提供“遍历”操作及iterators。按正常规则(++ite)遍历,便能获得排序状态(sorted)。
我们不应使用rb_tree的iterators改变元素值,因元素有其严谨排列规则。但编程层面并喂阻绝,因为rb_tree会作为set和map的底层支持容器,而map允许元素的data被改变,只有元素的key是不能被改变的。
Rb_tree提供了两种插入元素的操作:insert_unique()表示节点的key一定在整个tree中独一无二,否则安插失败;insert_equal()表示节点的key可重复。
这里值得注意的是,在C++标准库中,key和data合成value。
四.容器set,multiset
Set和multiset以rb_tree为底层结构,因此有元素自动排序特性,排序依据是key。set和multiset元素的value和key合一,其value就是key。
同样,set和multiset提供“遍历”操作及iterators,按正常规则(++ite)遍历,便能获得排序状(sorted)。
我们无法使用set和multiset的iterators改变元素值,因其key和value合一。Set和multiset的iterator是其底层容器rb_tree的const-iterator,就是为了禁止对元素进行赋值操作。
Set元素的key必须独一无二,因此其insert()用的是rb_tree的insert_unique;multiset元素的key可重复,因此其insert()用的是rb_tree的insert_equal().
五.容器map,multimap
同样以rb_tree为底层结构,可以进行“遍历”操作有iterators Map/multimap和set/multiset的不同在于使用map/multimap的iterators无法改变元素的key,但可以改变其data,key和data合成了元素的value。
Map元素的key必须独一无二,因此其insert()用的是rb_tree的insert_unique();multimap元素的key可以重复,因此其insert()用的是rb_tree的insert_equal()
这里值得注意的是,容器map有其独特的operator[]:lower_bound试图在sorted()中寻找元素value,若找到,便传回与value相等的第一个元素;若未找到,则传回该map中最适合放这个元素的位置,在这个点插入新元素。
六.容器hashtable
在本课程第一周的学习中,就对哈希表有了粗略的了解。当容器空间足够时,元素以一个编号T安插在容器中的某个位置,当容器空间不足时,相同的编号便会发生碰撞,通过key/M的余数放在相应的位置。
发生碰撞便安插链表,如果元素个数超过了bucket的个数,则将bucket扩大一倍,所有元素打散重新排列,这叫做rehashing。
Rehashing扩大后的bucket个数刻意为扩大两倍数附近的质数:53、97、193……
Hashtable涉及到的一些类如下代码所示,其中HashFcn用来提取元素的key:
哈希表的节点由两部分组成:值+单向链表指针
这里值得注意的是:iterators走到单向链表边缘时++ite后必须能够到达下一个bucket。因此其iterator中的node* cur指向当前的元素,同时,hashtable* ht指向hashtable本身,即这些bucket
最后,由hash-function算出每个元素的hash-code,使得这些元素经过hash-code映射之后能够足够杂乱随机地置于容器hashtable内,越是杂乱随机,元素之间则越不容易发生碰撞。模板库中Hash-function对各种数据类型进行了偏特化,不同的数据类型对应不同的算法得到hash-code
七.Unordered容器
C++11将hash_***容器改为了unordered_***容器,但本质上并没有区别,unordered容器仍然是建立在哈希表的基础之上的。