zoukankan      html  css  js  c++  java
  • STL源码剖析之五:关联式容器

    关联式容器   

    所谓关联式容器,观念上类似于关联式数据库:每笔数据都有一个键值(key)和一个实际值(value)。当元素被插入容器时,内部机制根据键值,按着一定的规则将元素置于特定的位置。关联式容器没有所谓头尾的概念(只有最大元素,最小元素),所以不会有类似push_back(),push_front()这样的操作。

    标准的stl关联式容器分为set和map两大类,以及这两大类的衍生体multiset和multimap,这些容器的底层机制均以RB-tree来实现,RB-tree也是一个独立容器,并不开放给外界使用。此外,SGI STL还提供了一个不在标准之内的关联式容器hash-table,以及以之为底层机制而完成的hash-set,hash-map,hash-multiset,hash-multimap。  

    二叉搜索树

    一般而言,关联式容器的内部结构是二叉搜索树(binary balanced tree),BBT有很多种类,包括RB-Tree,AVL-Tree,AA-Tree,其中最被广泛运用的是RB-tree。

    二叉搜索树的的规则是:任何结点的键值都大于其左子树中每个结点的键值,而小于其右子树中每一个结点的键值。二叉搜索树支持的操作时间复杂度与树的高度成正比,如果树是平衡的(极端是完全二叉树),其操作效率就高,不平衡(极端是单链),其操作效率就低。为了保证操作的效率,因此有所谓平衡二叉树的概念。

    平衡二叉树是施加了某些条件来保证平衡性的二叉搜索树,关于AVL树和RB-TREE的定义和如何保证平衡性,可以参考《算法导论》或其他介绍数据结构的书籍。

    AVLtree的平衡性要求比RB-Tree要严格,至于STL为何选择RB-Tree作为关联式容器的底层机制,而不是AVL-Tree,我认为应该是由于保证平衡性本身要增加一定的算法复杂度,要求越严格,操作过程中对树的调整就越频繁。RB-tree在两个指标之间达到了比较好的折中,实际效果要好于AVL-Tree。

    Set和MutiSet

    set是集合,它的元素的键值就是实值,实值就是键值,不允许两个元素有相同的值。

    我们不可以通过set的iterator来改变元素的值,因为set的元素值就是键值,改变键值会违反元素排列的规则。

    在客户端对set进行插入或删除操作后,之前的迭代器依然有效(当然被删除那个元素的迭代器除外)。

    STL还提供了一些集合算法包括交际、并集等。

    set底层机制是RB-tree,所有的操作都只是转掉RB-tree的操作行为而已。

    MultiSet和set几乎一样,唯一的区别是,multiset允许键值重复。

    Map和MultiMap

    Map的元素都是pair,第一个值是键值,第二个是实值。

    我们可以通过map的迭代器来改变元素的实值。

    MultiMap和Map几乎一样,唯一的区别是允许键值重复。

    HashTable

    Hash table可以提供对任意有名项的存取和删除操作,这种结构的用意在于提供常数时间的的基本操作。

    stl hash table采用的hash方式是开链法。

    hash table的存储结构分成两级,第一级是连续空间的桶子(buckets),每个桶子里面是一个结点链表。桶子列表是用stl vector来实现的,而链表结构和list或slist没有关系。

    hash table的迭代器没有定义后退操作。

    stl hash table的模板参数非常多:

    Value:实值类型;

    Key:键值类型;

    HashFun:将键值转化为hash值的函数(注意这个不是通常所说的映射地址的哈希函数)。

    ExtractKey:从结点中取出键值的方法

    EqualKey:判断键值是否相等的函数

    Alloc:空间配置器,默认std::alloc

    虽然开链法并不要求表格大小为质数,但stl任然使用质数。并先将28个质数准备好,以备随时访问。当需要初始化或扩张表格时至n时,就设定表格实际大小为最接近且不小于n的质数。

    stl hash table扩张表格的触发条件是:当元素的数目大于或等于表格的大小。(这个条件应该是为了保证常数操作时间,在统计基础上得出的)。

    Hash_Map,Hash_Set,Hash_MultiSet,Hash_MultiMap

    这些容器和前面介绍的一一对应,只不过这些是以hash_tabel为底层实现机制的。

    底层机制决定了这两组容器的区别:

    RB-tree组对元素实现排序,而hash_map组没有;

    RB-tree的查找时间复杂度为lg(n),而hash_map组为常数时间;

    RB-tree组在空间利用上,不会浪费结点,而hash_map组可能会有一些空置桶。

  • 相关阅读:
    PCL中分割_欧式分割(1)
    如何在ROS中使用PCL(2)
    PCL超体聚类
    PCL常见错误集锦
    cv_bridge中的编码模式与实现
    Centos 安装配置gerrit
    git merge git pull时候遇到冲突解决办法git stash
    Python 虚拟环境:Virtualenv
    配置gitlab gerrit jenkins
    selinux开启关闭
  • 原文地址:https://www.cnblogs.com/longhuihu/p/10423353.html
Copyright © 2011-2022 走看看