STL源码剖析 笔记之五 关联式容器

第五章 关联式容器
标准的STL关联式容器分为set和map两大类,以及这两大类的衍生体multiset和multimap。
这些容器的底层机制均以RB-tree完成。 RB-tree也是一个独立容器,但是不开放给外界使用。

所谓关联式容器,观念上类似关联式数据库:每个元素都有一个键值(key)和一个实值(value)。
当元素被插入到关联式容器中时,容器内部结构便依照其键值大小,以某种特定规则将这个元素放置于适当位置。

一般而言,关联式容器是一个平衡二叉树,以便获得良好的搜寻效率。
平衡二叉树包括AVL-tree,RB-tree,AA-tree。
树的总览
1. 二叉搜索树
提供对数时间的元素插入和访问
二叉搜索树的规则:任何节点的键值大于左子树的每一个节点键值,小于右子树的每一个节点的键值。
取最大最小值:沿根节点一直往右得最小值,一直往左得最大值。
元素插入:从根节点开始比较,新数据小则往右走,大则往左走,直到尾端,即可插入。
元素移除: 1.若只有一个子节点,则取而代之。
          2.若有两个子节点,取右子树中的最小值,代替之。
缺点:可能失去平衡
2.平衡二叉搜索树
为了维护平衡,插入和移除操作可能耗时较长,但节省了访问时间。
①AVL tree  加上了额外平衡条件的二叉搜索树,为了确保整棵树的深度为O(logN)。
规则:要求 任何结点的左右子树高度差不大于1。

AVL树在新插入节点后,若失去平衡, 只要调整“从根节点到插入点”路径上,平衡状态被破坏的节点中 最深的那一个即可。
调整方式:
单旋转
双旋转
②RB tree SGI STL中唯一实现的搜寻树。<stl_list.h>
规则:1.每个节点都有颜色,非红即黑。
     2.根节点为黑色。
     3.父子节点不得同时为红。
     4.任何结点到NULL(树尾端)的任何路径,所含黑节点数目必须相同。
     为了方便,NULL被视为黑色。
根据规则4,新增节点必须为红;根据规则3,新增节点的父节点必须为黑。
插入节点, 若不满足RB树的规则,则需要调整颜色并旋转树形。
调整方式:

RBtree的结点
struct __rb_tree_node_base
{
  color_type color; 
  base_ptr parent;
  base_ptr left;
  base_ptr right;
};

template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
  Value value_field;
};
RBtree的迭代器
struct __rb_tree_base_iterator
{
  __rb_tree_node_base* node;

  void increment();//
  void decrement();//
};
template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
    ...
  reference operator*();
  pointer operator->(); 
  self& operator++() ;
  self operator++(int); 
  self& operator--(); 
  self operator--(int); 
};

RBtree的数据结构

template <class Key, class Value, class KeyOfValue, class Compare,
          class Alloc = alloc>
class rb_tree { // 只有3个数据
            size_type node_count; 
            link_type header;//技巧?
            Compare key_compare;//用来比较键值大小的仿函数
}
RBtree的构造和内存管理
有一个专属的空间配置器rb_tree_node_allocator,用来配置一个节点大小的空间。
构造方式:1.以先有的RBtree复制一棵树;2.生成一棵空树。
空树只有一个header节点,父节点为NULL,子节点均指向自己,颜色为红。
非空树 header与root互为父结点,header左右子节点分别指向极小值和极大值。

RBtree的元素操作
插入 
insert_unique() 键值不可重复
若重复则,插入无效,返回一个pair,第一个元素是RBtree迭代器,第二个元素表示是否成功。
insert_equal()  键值可重复
大则往左,小于等于往右。返回一个RBtree迭代器,指向新增节点。
__insert()             
__rb_tree_rebablance() 
调整RBtree 

元素搜寻
find()

1.set 实现于<stl_set.h>
set的特性是,所有元素都会根据元素的键值被自动排序。
set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。
set iterators是一种constant iterators,杜绝写入。
template <class Key, class Compare, class Alloc = alloc>
class set {
private:
  typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing set
}
STL提供了一组set/multiset相关算法,包括交集,联集,差集,对称差集。
2.map   实现于<stl_map.h>
map的特性是,所有元素都会根据元素的键值被自动排序。
map的元素都是pair(定义在<stl_pair.h>),同时拥有实值和键值。
pair的第一元素为键值,第二元素为实值。
template <class T1, class T2>
struct pair {
  T1 first;
  T2 second;
};
map不允许两个元素有同样的键值。
可以通过迭代器修改元素的实值,但不能修改键值。
map iterators既不是constant iterators,也不是mutable iterators。
  T& operator[](const key_type& k) {
    return (*((insert(value_type(k, T()))).first)).second;
  }
3.multiset 实现于< stl_multiset.h>
multiset的特性以及用法和set完全相同,唯一的差别在于它允许键值重复。
它的插入操作采用的是RB-tree的insert_equal()而不是insert_unique()。
4.multimap 实现于<stl_multimap.h>
multimap的特性以及用法和map完全相同,唯一的差别在于它允许键值重复。
它的插入操作采用的是RB-tree的insert_equal()而不是insert_unique()。

SGI STL还提供了非标准的关联式容器hash table,以及以此为底层机制而完成的hash_set,hash_map,hash_multiset,hash_multimap。
5.hash table 实现于<stl_hashtable.h>
二叉搜索树具有对数平均时间的表现,但是这样的表现构造在一个假设上:输入数据具有足够的随机性。
hash_table也具有对数平均时间的表现,而且这种表现以统计为基础,不依赖数据的随机性。
hash_table原理:
首先我们需要一个映射函数,把元素映射为在索引范围内的数值,这样的函数称为hash function。
使用hash function的问题:导致碰撞,也就是不同的元素被映射到相同的位置。
避免碰撞方法:线性探测、二次探测、开链
线性探测:当hash function计算出的某个元素的插入位置,已经不可用,则循序向下一一寻找可用空间。
二次探测:当hash function计算出的某个元素的插入位置,已经不可用,则循序尝试H+i^2位置。
开链:为每个表格元素维护一个list,在此list身上执行元素的增删改查等。SGI STL采用开链设计。

hash_table的节点
template <class Value>
struct __hashtable_node
{
  __hashtable_node* next;
  Value val;
};  
hash_table的迭代器
template <class Value, class Key, class HashFcn,
          class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {
...
  node* cur; //当前节点
  hashtable* ht;//哈希表指针,为了方便从一个bucket调至下一个bucket
...//没有后退操作operator--
}
hash_table的数据结构
template <class Value, class Key, class HashFcn,
          class ExtractKey, class EqualKey,
          class Alloc>
class hashtable {
...
private:
  hasher hash;
//散列函数
  key_equal equals;
//判断键值是否相同的函数或仿函数
  ExtractKey get_key; //从节点中取出键值的函数或仿函数

  vector<node*,Alloc> buckets;//主存储位置,每个节点附带一个list
  size_type num_elements; //元素总数
...
}
虽然开链法并不要求表格大小必须为质数,但SGI STL仍然以质数设计表格。
hash_table元素的插入与表格重整
首先判断是否需要扩充表格,resize()(元素个数>vector容量则重整,是否为了保证负载系数小于1)
bkt_num决定要插入的元素处于那个bucket下,然后遍历list,如果允许重复,查找是否存在,存在则插入到相同元素的后面并返回;如果不允许重复,查找是否存在,存在则返回。如果未退出,则插入到该list的头部位置。

整个hash table由vector和linked-list组成,复制和整体删除,都需要特别注意内存的释放问题。
hash functions 实现于<stl_hash_fun.h >
对字符串的处理
inline size_t __stl_hash_string(const char* s)
{
  unsigned long h = 0; 
  for ( ; *s; ++s)
    h = 5*h + *s;
  
  return size_t(h);
}
6.hash_set  实现于< stl_hash_set.h >
以hash table为底层机制,所有的操作几乎都为转调hash_table相应函数。hash set没有自动排序功能。
7.hash_map   实现于< stl_hash_map.h >   
以hash table为底层机制,所有的操作几乎都为转调hash_table相应函数。hash map没有自动排序功能。
8.hash_multiset  实现于< stl_hash_set.h >
hash_multiset与hash_set的唯一区别在于,插入时候底层调用的是insert_unique(),而不是insert_equal()。
9. hash_multimap  stl_hash_map.h   实现于< stl_hash_map.h > 
hash_multimap与hash_map的唯一区别在于,插入时候底层调用的是insert_unique(),而不是insert_equal()。

本章节中关于AVL树的旋转,以及RBtree的平衡调整。后面再深入源码去解析。
整个hash table由vector和linked-list组成,复制和整体删除,都需要特别注意内存的释放问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值