STL-关联式容器

STL——关联式容器

容器分类:序列式和关联式两种。

序列式:array(数组)、vector(向量)、heap(堆)、priority-queue(优先级队列)、deque(双端队列)、slist、list(链表)、stack(堆栈)、queue(队列)、

关联式::set(集合)、map(映射表)、multiset(多键集合)、multimap(多键映射表)。底层实现机制为RB-tree(红黑树)。扩展包括hash table(散列表)和以hash table为底层机制而完成的hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多键集合)、hash_multimap(散列多键映射表)。

下面针对关联式容器的相关知识进行总结。

Rb-tree

RB-tree(红黑树)是一种被广泛使用的平衡二搜索树。RB-tree必须满足以下规则:

1)每个节点不是红色就是黑色;

2)根节点为黑色;

3)如果节点为红,其子节点必须为黑;

4)任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同。

根据规则4),新增节点必须为红,根据规则3),新增节点之父节点必须为黑。当新节点根据二叉搜索树的规则到达其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。

红黑树:通过一些着色法则确保没有一条路径会比其它路径长两倍,从而达到接近平衡目的。

红黑树之所以为红黑树的原因:红黑颜色用来检测树的平衡性,达到AVL树的平衡要求,降低了对旋转的要求,从而提高了统计性能。红黑树相对AVL树能够给我们一个比较便宜的解决方案。红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更好。

Rb-tree不仅在树性的平衡上表现不错,在效率表现和实现复杂度上也保持相当的平衡,所以运用甚广。主要用于存储有序的数据,它的时间复杂度为O(logn)效率非常之高,Java集合中的TreeSet和TreeMap,C++的STL中的set,map以及Linux虚拟内存的管理,都通过红黑树去实现。

算法导论中对红黑中理解:

性质:

1)每个节点或是红色,或是黑色;

2)根节点是黑色的;

3)每个叶节点(NIL)是黑色的;

4)如果一个节点是红色的,则它的两个子节点都是黑色的;

5)对每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点。

红黑树性质:一棵有n个内部节点的红黑树的高度至多为2lg(n+1)

红黑树中插入一个节点,破坏红黑树的性质是:2)根节点是黑色和4)如果一个节点是红色的,则它的两个子节点都是黑色的。红黑树插入后调整,主要保证以下三点:1)节点z是红节点;2)如果z.p是根节点,则z.p是黑节点;3)如果有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2,或是性质4。若果性质2被破坏,其原因为z是根节点且是红色节点。如果性质4被破坏,其原因为z和z.p都是红色节点。

主要包括三种情况处理:

1)z的父节点为红色,z的叔父节点也是红色。处理:改变z的父节点,叔父节点和祖父节点,然后将z提升到z的父节点位置,进行下一步判断;

2)z的父节点为红色,z的叔父节点为黑色,且z是一个右孩子。处理:将z提升到z的父节点,然后执行左旋操作,进行下一步判断;

3)z的父节点为红色,z的叔父节点为黑色,且z是一个左孩子。处理:改变z的父节点和z的祖父节点颜色,然后执行右旋操作,进行下一步判断;

(注意,上面处理时针对z的父节点是z的祖父节点的左子树进行的,当其为祖父节点的右子树,则只需改变左右操作即可)

红黑树删除基本思想是:删除后,用其子树替换,这部分与二叉搜索树的删除的思想本质一样,但是红黑树删除后,可能会破坏红黑树的性质,此时就需要进行树的调整操作即可。

Set

set特性:所有元素都会根据元素的键值自动被排序。set的元素不像map那样,可以同时拥有实值和键值,set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。

不可以通过set的迭代器改变set的元素值。set拥有与list相同的性质:客户端对它进行元素新增操作或删除操作时,操作之前的所有迭代器,在操作完成之后依然有效。

set的底层实现机制是红黑树。set提供的算法包括交集、并集、差集、对称差集等。

multiset

multiset的特性及用法和set完全相同,唯一的差别在于它允许键值重复,因此的它的插入操作采用的是底层机制Rb-tree的insert_equal()而非insert_unique()。

map

map特性:所有元素都会根据元素的键值自动被排序。map的所有元素都是pair,同时拥有实值和键值。pair的第一个元素被视为键值,第二个元素被视为实值。map不允许两个元素拥有相同的键值。

可以通过map的迭代器改变map元素的实值,因为元素的实值不影响map元素的排列规则。但通过map迭代器改变map的键值是不行的,因为map的键值关系到map元素的排列规则,任意改变map元素键值将会严重破坏map组织。

map和list拥有相同的性质:当客户端对它进行元素新增操作或删除操作时,操作之前的所有迭代器,在操纵完成之后都依然有效。

map的底层实现机制是红黑树。

multimap

multimap的特性以及用法与map完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制Rb-tree的insert_equal()而非insert_unique()。

Hashtable

hashtable(散列表)数据结构,在插入、删除、搜寻等操作上也具有“常数平均时间”的表现,而且这种表现是以统计为基础,不需仰赖输入元素的随机性。

hashtable的操作对象是有名项,故也被视为一种字典结构,这种结构用于提供常数时间之基本操作。但可能需要额外的空间和初始化时间。映射函数:将大数映射为小数。负责将某一元素映射为一个“大小可接收之索引”。hash函数存在问题:可能有不同的元素被映射到相同的位置(有相同的索引),即碰撞。决绝哈希碰撞方法:线性探测、二次探测、开链等。

线性探测

负载系数:指元素个数除以表格大小。负载系数永远在0~1之间,除非采用开链策略。线性探测需要两个假设:1)表格足够大;2)每个元素都够独立。hash过程中的主集团指平均插入成本的成长幅度,远高于负载系数的成长幅度。

二次探测

解决主集团问题,由于解决碰撞方程为F(i)=i^2是二次方程式,故为二次探测。二次集团可能引起次集团问题,即两个元素经hash函数计算出来的相同位置若相同,则插入时所探测的位置也相同,形成某种浪费。消除次集团的办法可是使用复式散列。

开链法

此方法是在每个表格元素中维护一个list,hash函数为我们分配某一个list,然后我们在那个list身上执行元素的插入、搜寻、删除等操作。虽然针对list进行搜寻只能是一种线性操作,但如果list够短,速度还是够快。

hashtable的桶子与节点:hashtable表格内的元素为桶子,其意义是表格内的每个单元,涵盖的不只是个节点(元素),甚至可能是一个“桶节点”。hashtable的迭代器没有后退操作(operator–()),hashtable也没有定义所谓的逆向迭代器(reverse Iterator)

hash_set

hash_set是以hashtable为底层机制。因hash_set所供应的操作接口,hashtable都提供了,所以几乎所有的hash_set操作行为,都只是转调用hashtable的操作行为。

set是为了能够快速搜寻元素。其底层是Rb-tree有自动排序功能,但hashtable没有自动排序功能,故hash_set没有自动排序功能。hash_set和set一样,元素的键值就是实值,实值就是键值。hash_set和set使用方式基本相同。

hash_multiset

hash_multiset和multiset完全相同,唯一差别是底层实现机制不同,hash_multiset的底层实现机制是hashtable,但multiset的底层实现机制是Rb-tree。hash_multiset和hash_set实现上的唯一差别是,hash_set的插入操作采用hashtable中的insert_unique(),而hash_multiset的插入操作采用hashtable中的insert_euqal()。hash_multiset和hash_set使用方式基本相同。

hash_map

hash_map的底层实现机制也是hashtable。故hash_map所供应的操作接口,hashtable都提供了,所以几乎所有的hash_map操作行为,都只是转调用hashtable的操作行为而已。

map能够根据键值快速搜索元素,其底层实现机制是Rb-tree,Rb-tree具有自动排序功能,故map具有自动排序功能,但hashtable灭有自动排序功能,故hash_map没有自动排序功能。

hash_map和map都有相同的特性,即每一个元素都同时拥有一个实值(value)和一个键值(key)。hash_map和map使用方式大体相同。

hash_multimap

hash_multimap的特征与multimap完全相同,唯一差别为它的底层实现机制是hashtable,故hash_multimap的元素并不会被自动排序。hash_multimap和hash_map实现上的唯一差别是,hash_multimap的插入操作使用底层机制hashtable中的insert_equal(),而hahs_map使用的是底层机制hashtable中的insert_unique()。hash_multimap使用方式与hash_map完全相同。

参考:

[1] STL源码剖析 侯捷

[2] 算法导论

你可能感兴趣的:(STL)