第五章: 关联式容器 associative containers
1、标准STL的关联容器: set multiset(多键集合) map(映射表) multimap(多键映射表)
这些容器的底层机制都是RB_Tree(红黑树)完成。RB_Tree也是一个独立容器,但不开放给外界使用。
2、此外,SGI STL还提供了一些关联容器:hashtable(散列表,已引入C++11),并以hash table 为底层机制完成了hash_set(散列集合)、hash_map(散列映射表)、hash_multimap、hash_multiset。 散列表是一种很重要的容器。
3、所谓关联式容器:每个元素都有一个键值(key),和一个实值(value),
Set 的键值就是实值,map的键值和实值可以分开,而形成映射表。 当元素被插入到容器中时,按照简直大小。一某种特定的规则放到合适的位置。关联式容器没有头尾之分,只有最大最小元素,因此也就没有专门正对头尾的操作函数。
5.1、树的导论:
1、二叉搜索树:
二叉树的递归定义:一个二叉树如果不为空,便由一个根节点和左右子树构成;左右子树都可能为空。、
二叉搜索树:可提供对数时间的元素插入和访问。 其节点放置规则:任何节点的值(注:键值下同)一定大于其左子节点的值,而小于其右子节点的值。
二叉搜索树的基本操作:
查找最大 (根节点一直右走)
最小值| (根节点一直左走)、
插入新元素(从根节点开始比较,遇较大值往左,遇较小值往右,一直到达尾端即为插入点)、
删除元素(只有一个子节点):直接将直接点移至该节点处。
删除元素(两个子节点): 以右子树的最小节点移动该节点处。
2、平衡二叉搜索树: 维持一定的平衡条件。不同的平衡条件造就不同的效率表现和不同的实现复杂度。如:AVL_tree RB_tree AA_tree等特殊结构。
AVL——tree: 其平衡条件的建立是为了确保整棵树的深度为对数深度(O(logN))。及要求:任何子树的左右子树高度相差最多为一。 在插入新节点时,可能不符合高度相差最多为一的情况,应该需要调整(单旋转、双旋转)
5.2、RB_tree
RB_tree不仅是搜索二叉树,而且满足:
1)、每个节点不是红就是黑(加一个底色属性)
2)、根节点为黑色。
3)、父子两节点不能同时红。如果节点为红则其子节点必须为黑。(即子节点为红,则父节点比为黑,即其一红,另一必黑)
4)、任一节点至NULL(树尾端)的任何路径,所有黑节点数必须相同。
因此由4知,新增节点必为红,进而由3知,新增节点之父节点比为黑。
当新增节点按二叉搜索树的规则到达插入点,却不符合上述要求时,就必须调整颜色并调整树形。
RB——tree的迭代器: 双向迭代器,但无随机定位功能。
RB_tree_base_node RB_tree_node;
RB_tree_base_iterator, RB_tree_iterator;
RB_tree:封装节点类、迭代器类、空间配器器、节点操作函数等。
5.3、set
1、STL set是以RB_tree为底层机制,调用其接口实现的。 Set的所有元素都是根据其键值排好序的。 键值即为其实值。RB_tree为平衡二叉搜索树,自动排序的效率较高。
2、Iterator:由于其元素排好序的,因此不能通过迭代器改变元素的键值即实值。故而,set的iterator为const_iterator(实际是定义为底层的RB_tree的const_iterator),即不能改变迭代器所指之值,以免破坏set的数据结构。
3、对set进行插入、删除操作迭代器不会失效,除被指之元素被删除者!
4、注意:set使用的是RB_tree的insert_unique()插入,因为set不允许相同键值的元素存在。 Multiset才用insert_equal()插入操作。
5.4、map
1、STL的map也是基于RB_tree为底层机制实现的。 所有元素按照键值自动排序。其元素都是pair,pair的第一个元素为键值,第二个为实值。 Map不允许两个元素有相同的键值。
2、map的iterator:迭代器既不是mutable iterator类型,也不是 constant iteator类型,只能通过迭代器改变元素的实值,不能改变其键值。
3、对map的插入和删除操作除被删除元素之迭代器外,迭代器不会失效。
5.5、multiset:
Multiset与set的唯一差别在于:允许键值重复,因此用的是底层RB_tree的inert_equal()插入操作。其他与set完全相同。
5.6:multimap
Multimap与map的唯一差别在于:允许键值重复,因此用的是底层RB_tree的inert_equal()插入操作。其他与set完全相同。
5.7、 hashtable
1、hashtable可被视为一种字典结构,这种结构在于提供常数时间之基本操作。
2、散列函数hashfunction:把元素值对应到表格的某一位置,保证所有元素都能存放在有限长度的表格中。
3、具体的对应方法:线性探测,二次探测,开链。 SGI STL就是采用的开链法(多个元素可能对应到表格的同一位置)。
4、开链:每一个表格元素都维持一个list(存放对应到同一个位置的多个元素),然后表格的操作最终落到对每个局部list的操作,以及各list之间的切换。(list虽然为线性操作,但当够短时,速度够快!)
5、也因此hashtable的元素(节点)称为“桶节点“bucket(里面可以装有多个节点).
Hash table的节点:维护一个next指针,以便在桶内节点移动。和一个value值
Hash table的iterator:为前向迭代器类型。
维护一个当前节点指针node*,以及一个hashtable的容器指针(以便在每个桶节点间移动)。 以及在桶类节点、桶之间的前进移动(operator++())。
hashtable的数据结构:
1、hashtable的buckets集合体以vector完成,以利于动态扩充。Vector的元素为bucket node的指针。
2、SGI中以hast table 为底层机制实现的:
Hash_set hash_map hash_multiset hash_multimap容器:
注意:由于hashtable不像RB_tree那样具有自动排序功能,因此基于hashtable的这些容器也就不具有自动排序功能。