您的位置:首页 > 财经 > 产业 > C++ STL 关联容器

C++ STL 关联容器

2024/12/23 6:23:30 来源:https://blog.csdn.net/surfaceyan/article/details/127414434  浏览:    关键词:C++ STL 关联容器

系列文章目录

C++STL迭代器iterator设计 https://blog.csdn.net/surfaceyan/article/details/126772555
C++ STL 序列式容器(一 vector list) https://blog.csdn.net/surfaceyan/article/details/126860166
C++ STL 序列式容器(二 deque slist) https://blog.csdn.net/surfaceyan/article/details/127083966
tinystl_list Debug https://blog.csdn.net/surfaceyan/article/details/128105082


C++11 标准库头文件模拟实现,无锁STL https://blog.csdn.net/surfaceyan/article/details/139887490


文章目录

  • 系列文章目录
  • ● 一、树
    • 基本概念
    • 二叉搜索树 binary search tree
    • 平衡二叉搜索树 balanced binary search tree
    • AVL tree (Adelson-Velskii-Landis tree)
      • 平衡破环情况
      • 单旋转(Single Rotation) 左左,右右
      • 双旋转(Double Rotation)
    • RB-tree(红黑树)
      • 插入节点
      • 由上而下的程序
    • RB_tree的设计
    • RB-tree插入示例
  • 二、set, map, multiset, multimap
  • 三、hashtable
    • hashtable的桶(buckets)与节点(nodes)
  • 总结


● 一、树

基本概念

树由节点(nodes)和边(edges)构成。整棵树有一个最上端节点,称为根节点(root)。每个节点可以有具有方向性的边(directed edges),用来和其它节点相连。父节点(parent),子节点(child)。无子节点者称为叶子节点(leaf)。子节点可以存在多个,如果最多只允许两个子节点,即所谓二叉树(binary tree)。不同的节点如果拥有相同的父节点,则彼此互为兄弟节点(siblings)。根节点至任何节点之间有唯一路径(path),路径所经过的边数,称为路径长度(length)。根节点至任意节点的路径长度,即所谓该节点的深度(depth)。根节点的升读永远是0。某节点至其最深子节点(叶节点)的路径长度,称为该节点的高度(height)。整棵树的高度,便以根节点高度来代表。节点A->B之间如果存在(唯一)一条路径,那么A称为B的祖代(ancestor),B称为A的子代(descendant)。任何节点的大小(size)是指其所有子代(包括自己)的节点总数。
在这里插入图片描述

二叉搜索树 binary search tree

任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。

  • 1
    所以,从根节点一直往左走,直至无路可走,即得最小元素;从根节点一直往右走,直至无路可走,即得最大元素。下图为二叉搜索树
    在这里插入图片描述
  • 2 元素插入
    插入新元素时,从根节点开始,遇键值较大得就向左,遇到键值较小得就向右,一直到尾端,即为插入点。
    在这里插入图片描述
  • 3 元素删除
    1)当A只有一个子节点时,直接将A得子节点连接至A得父节点,并将A删除
    2)A有两个子节点,以右子树内得最小节点取代A。注意右子树得最小节点为:从右子节点开始以指向右走至底即是。
    在这里插入图片描述

二叉搜索数还有个特点:树的节点从最左边到最右边是递增的
在这里插入图片描述

平衡二叉搜索树 balanced binary search tree

二叉搜索树可能失去平衡造成搜寻效率低落得情况,极端情况下二叉搜索树退化为链表。

“平衡”的大致意义是:没有任何一个节点过深。不同的平衡条件,早就处不同的效率表现,以及不同的实现复杂度。有数种特殊结构如AVL-tree, RB-tree, AA-tree,均可实现处平衡二叉搜索树。

AVL tree (Adelson-Velskii-Landis tree)

AVL tree是一个“加上了额外平衡条件”的二叉搜索树。
平衡: 任何节点的左右子树高度相差最多1,若无右(左)子节点则右(左)子树高度为0

当插入新元素11后平衡条件破坏
在这里插入图片描述

平衡破环情况

“插入前平衡,插入后不平衡” 的节点为X

  1. 插入点位于X的左子节点的左子树——左左
  2. 插入点位于X的左子节点的右子树——左右
  3. 插入点位于X的右子节点的左子树——右左
  4. 插入点位于X的右子节点的右子树——右右

单旋转(Single Rotation) 左左,右右

在这里插入图片描述

双旋转(Double Rotation)

图中C可能比B多一层(实际插入到了C子树的叶子节点),也有可能比B少一层(实际插入到了B子树的叶子节点)。
(图中ABCD子树高度为示意图)
在这里插入图片描述

RB-tree(红黑树)

红黑树(balanced binary search tree)是一个二叉搜索树,且必须满足如下规则:

  1. 每个节点不是红色就是黑色(深黑,浅红)
  2. 根节点为黑色
  3. 如果节点为红,其子节点必须为黑
  4. 任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同

总结:根节点为黑色,新节点必须为红色,并且只能插入在黑色节点下,若不满足插入条件需要调整红黑树

插入节点

  • 情况1
  • 情况2
  • 情况3
  • 情况4

由上而下的程序

RB_tree的设计

在root节点之前插入一个header节点:
在这里插入图片描述
在这里插入图片描述

在构造rb_tree是header节点随之建立

struct node
{node* parent;node* left;node* right;node_color color;V value;
};
header->parent = 0;  header->parent = root;

header的left指向整个数的最左边的节点,right指向最右边的节点,parent指向root
begin()返回header->left, end()返回header

RB-tree插入示例

一次插入10, 7,8,15,5,6,11,13,12

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、set, map, multiset, multimap

它们的底层实现都是基于红黑数的,所有的操作只是将其传递给红黑树,具体操作都是由该数完成。

typedef set/map/mulset/multimap container_typeclass container_type{
private:rb_tree t;
public:return_type insert(value_type){ insert_unique(x)(set,map)  insert_equal(x) (multixxx) }
};

三、hashtable

最简单的哈希表就是数组。将数组用作哈希有两个问题:1.如果元素是64-bits则数组大小过大。 2.如果key不是整数则无法将key作为索引。
对于第二个问题,可以将非整形的key编码为整形,如可将字符串“jjhou”编码为’j’*128^4 + ‘j’*128^3 + ‘h’*128^2 + ‘o’*128^1 + ‘u’*128^0 = 超级大的数
对于第一个问题,如果避免超级大的数组索引,使用hash function将其映射为小数。如 X % TableSize会得到一个[0, TableSize-1]之间的数。

引入hash必然会有哈希碰撞问题。

线性探测(linear probing):

负载系数(loading factor),元素个数/表格大小。负载系数一般在[0,1]
当哈希函数计算出某个元素的插入位置,而该位置上的空间已不再可用时,最简单的办法就是循序往下一一寻找(如果到达尾端就绕道头部继续寻找),直到找到一个可用空间为止。只要表格(array)足够大,总能找到一个可用空间,只是花费时间不确定。进行元素搜索时,如果哈希函数计算出来的位置上的元素值与我们的搜寻目标不符就一一往下搜索,直到找到或者遇上空格元素。元素的删除必须采用惰性删除,即只是标记删除记号,实际删除操作则等待表格重新整理(rehashing)时在进行。
在这里插入图片描述

二次探测:

二次探测主要用来解决主集团(primary clustering)的问题。其命名由来是因为解决碰撞问题的方程F(i) = i^2是个二次方程。如果哈希函数计算处新元素的位置为H,而该位置已被占用,那么则依次尝试 H+1^2, H+2^2, H+3^2, H+4^2, , , H+i^2,而不是像线性探测那样依次尝试H+1, H+2, H+3, H+4, … , H+i
在这里插入图片描述

开链(separate chaining):

每一个表格元素中维护一个list,哈希函数为我们分配一个list,然后在那个list中执行元素的插入、搜索、删除等操作。
开链法的负载系数会大于1。SGI STL的哈希表便是采用这种做法。

hashtable的桶(buckets)与节点(nodes)

开链法的hash table表格内的每个元素称为桶。
在这里插入图片描述

template<class Value>
struct __hashtable_ndoe{__hashtable_ndoe* next;Value val;
};

hashtable的元素插入示意图
unordered_map中的bucket个数为 1, 13,xxx 59等
当插入元素时会向对应的bucket中插
在这里插入图片描述
当负载系数达到要求后会触发重整:
在这里插入图片描述


总结

containerimpl
setrb-tree
multisetrb-tree
maprb-tree
multimaprb-tree
unordered_sethashtable
unordered_multisethashtable
unordered_maphashtable
unordered_multimaphashtable

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com