STL之顺序容器和关联容器总结


顺序容器

         Vector中所采用的数据结构非常简单:线性连续空间。当分配空间被占满而仍然需要添加元素时,vector便会进行一场空间重新配置的大工程!在这里,程序员需要注意的是,一旦引起空间重新配置,之前指向原vector的所有迭代器就都失效了,这一点在工程中容易引起bug

         List则对空间的运用有绝对的精准,一点也不浪费。注意,list内部构成的实际是一个环状的双向链表!所以只需要一个指针,便可以完整地表现整个链表。

         Deque相比于vector而言,它没有容量的概念,因为deque是动态地以分段连续空间组合而成,随时都可以增加一段新的空间并链接起来。为了使得deque在逻辑上看起来是连续空间,在STL内部实现实际是使用了一块map(不是STL中的map容器)作为主控,map是一小块连续空间,其中每个元素都是指针,指向另一段较大的连续线性空间,称为缓冲区,这些缓冲区才是真正存放deque元素的主体。


         Stack在缺省的情况下是使用deque作为底部结构,只需要封闭deque一端的操作即可,也可以指定其他的容器作为底部结构,例如list。

         同样,queue也是基于deque实现。

         Slist则是一个单向链表,没有push_back,只有push_front和pop_front。

         此外,还有heap和priority_queue,已经在其他文章有说明。

 

关联式容器

与之前的顺序容器有别的是,关联容器在存储时是以关键字key为下标进行存储的,标准的STL关联容器分为set和map两大类,之后的衍生版本有multiset和multimap,它们的区别是在存储时是否容许出现关键字key相同的情况。这些容器的底层机制均以RB-tree(红黑树)完成。此外,SGI STL还提供了一个不在标准规格之列的关联式容器:hash table,以及基于hash table而完成的hash_set和hash_map。但在C++11中,因为标准化的推进,unordered_map原来属于boost分支和std::tr1中,而hash_map属于非标准容器。尽量使用unordered_,代替hash_,二者在底层上实现机理是一样的

 

1.     Setmap

一般来说,关联式容器的内部结构是一个平衡二叉树,以便获得良好的搜寻效率,平衡二叉树有很多种类型,包括AVL-treeRB-tree等,最被广泛使用的是RB-tree

(1)   搜索二叉树,因为它的查找平均性能是O(lgN),因此查询中多用这种结构,但如果建立搜索二叉树的时候数据不够随机,可能会导致这棵二叉树出现左重右轻之类的失衡情况,这个时候平衡二叉树便体现出它的作用了。5

(2)   所谓的树形平衡与否,并没有一个绝对的测量标准,大致意义是没有任何一个节点过深。AVL树中定义是任何节点的左右子树高度相差最多为1,在插入新的元素的时候,可能会导致这棵AVL树失去平衡,这个时候就需要用到单旋转双旋转两种操作来调节AVL树使之重新平衡。

(3)   RB-tree是另一种二叉平衡树,它对平衡的定义更弱,它的规则如下:

A. 每个节点不是红色就是黑色

B. 根节点为黑色

C. 如果节点为红色,那么它的子节点必须为黑色

D. 任一节点至NULL的任何路径中,所含的黑色节点数必须相同

所以对于RB-tree的插入和删除是一个很复杂的过程,其中,插入有3中情况,删除有4中情况,但是它能很好地保证查询的效率,通常来说,比其他的平衡二叉树提高25%

RB-tree中,有两个函数很重要,一个是pair<iterator,bool>insert_unique(const Value_type &x),这个是将x插入到红黑树中,并且保持节点的独一无二(通常来说,如果使用insert函数在map插入新的元素,如果是重复元素,则不会真正插入,而是直接返回),返回值是一个pair类型,bool标志插入是否成功,iterator根据插入是否成功则指向新插入的节点或者NULL,另外一个是pair<iterator,bool> insert_equal(constValue_type &x),这个函数再插入节点时允许节点关键字key重复,这便是multisetmultimap实现的关键了,这也是它们和setmap最大的区别了。

       了解了红黑树的基本操作之后,就可以很自然地知道setmap的实现原理了,基本上setmap中的函数在RB-tree中均有实现,因此setmap只需要调用即可。值得注意的一个问题是,在关联容器中应该使用关联容器自身实现的find进行查找,因为它是充分利用了平衡二叉树的特性的,查找效率高,而普通的STL中的find算法则是循序查找。

 

2.     Hash_sethash_map

二叉搜索树具有对数平均时间的表现,但这样的表构建在一个假设之:输入数据足够随机。除此之外,还有另外一种数据结构,它在插入、删除、搜寻等操作上也具有常数平均时间,而且这种表现是以统计为基础,不需要依赖元素的随机性,它就是hash table

哈希表在处理冲突时有线性探测,二次探测以及开链等方式,SGI则是采用开链的方式,即将冲突的元素构成一个链表,在SGI提供的hashtable中表格内的元素为桶子(bucket),它是用vector来承载元素。

虽然在开链法中并不要求表格大小必须为质数,但是SGI STL仍然以质数来设计表格,它是将预先选取的28个质数保存在表中,当用户要求分配多少个桶时,则返回不小于且最接近的一个质数作为表格大小。

       Hash_sethash_map同样只需要调用hashtable中的函数即可。由于现在经常使用unordered_系列代替了hash_,所以对于hash_的具体用法就不赘述了。

 

 

阅读更多
文章标签: c语言 c++ 指针 stl
个人分类: C++基础知识
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭