STL源码剖析笔记-5关联式容器

5 关联式容器

5.1 树的导览

  • 路径长度(length):根节点至任何节点之间所浸过的边数
  • 深度(depth):根节点至任何一节点的路径长度,根结点的深度永远是0
  • 高度(height):某节点至其最深子节点的路径长度
  • 节点大小(size):所有子代(包括自己)的节点总数

5.1.1 二叉搜索树

  • 二叉搜索树的节点放置规则:任何节点的键值一定大于其左子树中每个节点的键值,并小于其右子树中每个节点的键值
  • 插入操作:插入新元素时,可从根节点开始,遇到键值较大的就向左,遇到键值较小的就向右,一直到尾端,即为插入点。
  • 删除操作:欲删除节点A,情况分为两种,如果A只有一个子节点,就直接将A的子节点连接至A的父节点,并将A删除;如果A有两个节点,就将A的右子树中的最小节点取代A.

5.1.2 平衡二叉搜索树

  • 平衡的大致意义是:没有一个节点的深度过深

5.1.3 AVL Tree

  • 任何节点的左右子树的高度差相差最多为1
  • 整棵树的深度为O(logN)
  • 平衡状态被打破时:只要调整“插入点至根节点”路径上,平衡状态被破坏的各节点中最深的那一个,便能使整棵树重新平衡。
  • 分为四种情况
    • 插入点位于x的左节点的左子树–左左–单旋转
    • 插入点位于x的右节点的右子树–右右–单旋转
    • 插入点位于x的左节点的右子树–左右–双旋转
    • 插入点位于x的右节点的左子树–右左–双旋转

5.1.4 单旋转

5.1.5 双旋转

5.2 RB-tree

  • 红黑树定义:

    1. 每个节点不是红色就是黑色
    2. 根节点是黑色
    3. 如果节点为红,其子节点必须为黑
    4. 任一节点值NULL的任何路径,所含黑节点数相同
  • 根据规则3,新增节点的父节点必须为黑,将NULL视为黑

  • 根据规则4,新增节点必须为红
  • 根据规则3,父子两节点不能同时为红
  • SGI STL 特别为根节点设计一个父节点header,它的左节点是整棵树的最左节点,右节点是整棵树的最右节点。
  • 红黑树插入与旋转见书…

5.3 set

  • set 不允许两个元素有相同的键值,默认是从小到大自动排序。
  • 不允许通过set的迭代器改变set的元素值,因为set的元素就是其键值,改变set元素值就会改变set的结构组织,所以set的迭代器是常量迭代器。
  • set底层是RB-tree,几乎其所有操作都是转调红黑树的操作。
  • 搜寻元素使用set提供的find()函数比STL的find()函数有效率。

5.4 map

  • map的所有元素都是pair
  • 不能通过迭代器修改键值,可以写改元素实值。
  • insert操作返回一个pair,由一个迭代器和一个bool值组成,bool值表示插入成功与否,插入成功迭代器指向被插入的元素,插入失败迭代器指向插入失败点的旧元素。
    operator[]的源码为:
T& operator[](const key_types& k) {
return (*((insert(value_type(k, T()))).first)).second;
}

5.5 multiset

  • 插入操作底层采用红黑树的insert_equal()而非insert_unique().

5.6 multimap

  • 插入操作底层采用红黑树的insert_equal()而非insert_unique().

5.7 hashtable

  • 碰撞问题:不同的元素被映射到相同的位置,即有相同的索引。解决碰撞问题的方法有:线性探测、二次探测、开链…
  • 负载系数:元素个数除以表格大小,除了开链以外,负载系数都是在0~1之间。
  • 线性探测:计算出某个元素的插入位置时发现该位置已经不可用,就循序往下一次寻找可用的位置,达到尾端就绕到头部去寻找。在表格足够大并且每个元素都是独立的假设下,线性探测的最坏情况是巡访整个表格,平均情况是巡访一半的表格。
  • 惰性删除:只标记删除记号,实际的删除操作则待表格重新整理时再进行。因为有些元素不仅表述了它自己,还关系到其他元素的排列。
  • 主集团:平均插入成本的成长幅度,远高于负载系数的成长幅度。如线性探测,好不容易在多次解决碰撞问题后插入了一个元素,却又增加了整个表格的插入成本。
  • 二次探测:线性探测是依次向后探测1,2,3…个位置,而二次探测是向后1,4,9,16…的位置,主要是为了解决主集团问题。
    • 如果表格大小为质数,并且负载系数小于0.5(大于就重新整理表格),可以保证每个新元素的探测次数不多于2.
  • 次集团:两个元素经哈希函数计算出来的位置若相同,则插入时所探测的位置也相同,形成某种浪费。利用再散列(double hashing)可以消除次集团。
  • 开链:在每个表格的元素中维护一个list。
    • 这里的list不是stl的list,而是自行维护的node,而表格则是使用vector.
    • 表格负载系数大于1,
    • STL的hash table就是采用开链。
  • 哈希表的迭代器没有后退操作(operator–()),也就没有逆向迭代器。
  • 哈希表的每个元素的list的最大容量就是表格vector的大小。当元素个数大于表格vector的大小时就需要重建表格。
  • 哈希函数可以处理的类型有:char*、const char*、char、unsigned char、signed char、short、unsigned short、int、unsigned int、long、unsigned long.像string、double、float这些类型是无法处理的需要用户自行定义哈希函数。

5.8~5.11 hash_set、hash_map、hash_multiset、hash_multimap

  • RB-tree有自动排序功能,而hashtable没有,因此反映出来的结果就是set、map、multiset、multimap的元素有自动排序功能而hash_set、hash_map、hash_multiset、hash_multimap没有。
  • hashtable有一些无法处理的类型,需要用户自己定义哈希函数,方式hashtable无法处理的,hash_set、hash_map、hash_multiset、hash_multimap也无法处理。
  • 几乎所有hash_set、hash_map、hash_multiset、hash_multimap的操作都是转调用hashtable的操作行为而已。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值