STL容器
在stl中容器分为两大类,序列式容器和关联式容器。
序列式容器:array、vector、heap、priority-queue、list、slist、deque、(stack、queue)最后两个是配接器
关联式容器:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap
1、vector
vector在数据安排和操作上和array非常相似。两者唯一的区别在于空间的运用的灵活性。array是静态空间,一旦配置了大小就不能改变了,如果要改变大小只能有客户端自己来操作。vector是动态空间,随着元素的增加,它的内部机制会自动扩充空间来容纳新的元素。在vector的实现技术中,关键在于其对大小的控制,以及重新配置时的数据移动效率。一旦vector旧的空间满载了,如果客户端每新增一个元素,vector内部只是扩充一个元素空间,那么效率并不能很好地提高,因为所谓扩充空间(无论多大),都涉及了“配置新空间、数据移动、释放旧空间”等几步,时间成本较高。vector采用的数据结构非常简单,线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已经被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端。为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端要求的量更大一些,以备将来可能的扩充。这便是容量的概念。换句话说,一个vector的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次在有新增元素,整个vector就得另觅居所。所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可配置新空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新的元素,并释放原空间。因此vector的任何操作,一旦引起空间重新配置,指向原vector的的所有迭代器就失效了,这是程序中很容易出现的错误,务必小心。
2、deque
vector是单向开口的连续线性空间,deque则是双向开口的连续线性空间。所谓双向开口,意思是可以在头尾分别作插入和删除操作。vector也可以在头尾两端进行操作,但是效率极差。
deque和vector的最大差异,一、deque允许于常数时间内对起头端进行元素的插入或是移除操作,二、deque没有所谓的容量观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。换句话,像vector那样,“因旧的空间不足而重新分配一块更大的空间,然后复制元素再释放空间”,这样的事情在deque中是不会发生的。也因此,deque没有必要提供所谓的空间保留功能。虽然deque也提供了Random Access Iterator,但是他的迭代器并不是依赖于普通的指针,其复杂度比vector复杂的多,所以也影响了哥哥运算层面。因此,不是十分必要的时候,我们尽量选择vector而不是deque,对于deque进行的排序操作,为了提高效率,可将deque完整的复制到vector中进行排序,然后将排完顺序的元素再复制到deque中。
deque是由一段一段的定量连续空间构成,一旦有必要在deque的前端或是尾端增加新的空间,便配置一段定量连续空间串接在deque的头端或是尾端,deque的最大任务便是在这些分段的定量连续空间上维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器架构。
受到分段连续线性空间的字面影响,我们可能以为deque的实现复杂度和vector相比虽不中亦不远,其实不然。主要因为,对于分段连续空间,必须有中央控制,而为了维持整体连续的假象,数据结构的设计以及迭代器前进后退等操作都颇为繁琐。deque的实现代码分量远比vector或是list都多得多。
3、stack
stack是一种先进后出的数据结构,他只有一个出口。stack允许新增元素,移除元素,取得最顶端元素。但是除了取得最顶端元素之外,没有任何其他的方法可以存取stack的其他元素。也就是讲stack不允许遍历行为。stack是以deque作为底层结构封闭其头端开口,便轻而易举形成了一个stack。stack不提供迭代器的功能,不能够走访数据,迭代访问stack.除了以deque作为stack的底层封装结构,有时也可以用list作为底层封装结构。
4、queue
queue是一种先进先出的数据结构,他有两个出口。queue允许新增元素,删除元素,从低端加入元素,从顶端移除元素。但是除了最低端可以加入、最顶端可以移除外,没有其他的方法存取queue中的元素。queue不允许遍历。stl中queue默认是使用deque作为queue的底层封装,封闭deque的底端出口和顶端入口,便很容易形成了一个queue。queue不提供遍历功能,也不提供迭代器。除了deque之外,也可以用list作为queue的底层封装结构。
5、heap
heap不属于stl的容器组件,他是一个幕后英雄,扮演着priority queue的助手。priority queue允许用户以任何次序将任何元素推入容器内,但是取出时一定是从优先权最高的元素开始取。binary max heap 正是具有了这种特性,适合作为priority queue的底层机制。 另一种比较时尚的做法是使用binary search tree作为priority queue的底层机制,这么一来,元素的插入和极值搜索就有了O(logn)的性能表现了,但是大材小用了,一来binary search tree的输入需要足够的随机性,二来是binary search tree并不容易实现。priority queue的复杂度,最好界于queue和binary search tree之间,才算是比较合适。因此binary heap便是比较合适的选择。
所谓的binary heap是一种complete binary tree(完全二叉树),也就是说整棵binary tree 除了最底层的叶节点之外都是满树,而底层的叶节点从左至右也没有间隙。由于整棵完全二叉树的这个特点,我们就可以利用array来存储所有的结点。当完全二叉树的某个节点位于array的i处,那么它的左孩子笔定位于2i处,右孩子必定位于2i+1处,它的父节点必定位于 [i/2] .因此就可以利用array很容易的模仿出一个完全二叉树了。然后利用array 加上一组heap算法,就很容易实现一个heap了。array的缺点是无法动态的改变大小,而heap却需要这样的功能,因此用vector来代替array。根据元素的排列方式,heap分为 max heap 和 min heap 两种,前者每个节点的键值都大于或等于其子节点的键值,后者每个节点的键值都小于或等于其子节点的键值。heap的所有元素都必须遵守特别的 cmplete binary tree排列规则,所以heap不提供遍历功能,也不提供迭代器。
6、priority queue
顾名思义,priority queue是一个拥有权值观念的queue,它允许加入新元素,移除旧元素,审视元素值等功能。由于这是一个queue,所以它只允许在底端加入元素,在顶端取出元素,除此之外别无其它存取元素的途径。priority queue带有权值观念,其内部的元素并非依照被推入的次序排列,而是自动依照元素的权值排列,权值越高,排在最前边。缺省情况下priority queue系利用了一个 max heap完成,后者是一个以vector表现complete binary tree。priority queue完全以底部容器 vector为根据,再加上heap的处理原则,所以其实现非常简单。queue以底部容器完成其所有的工作,这种以“修改某物接口,完成另一种风貌”被称之为配接器,因此 STL prority queue往往不被归类于容器,而是归类于 配接器。
7、slist
STL list 是一个双向链表,SGI STL 另外还提供了一个单向链表(single linked list)名为slist。这个容器并不在标准规格之内。 slist 和 list主要差别在于,前者的迭代器属于单向Forward Iterator,后者的迭代器属于双向的Bidiectional Iterator。为此,slist的功能自然也就受到了许多的限制。不过,单向链表所耗用的空间更小,某些操作更快。slist 和 list的具有共同的一个特色是,他们的插入insert、移除erase、接合splice等操作并不会造成原有的迭代器失效(当然,指向移除元素的那个迭代器,在移除元素之后就会失效)
关联式容器,类似于关联式数据库,每笔数据(每个元素)都有一个键值key和一个实值。当元素被插入到关联式容器中,容器内部结构(可能是RB-tree或是hash-table)便依照其键值的大小,以 某种特定的规则将这个元素放置于适当的位置,关联式容器并没有所谓的头尾,只有最大元素和最小元素。一般而言,关联式容器的内部结构是一个balanced binary tree(平衡二叉树),以便获得良好的搜寻效率。balanced binary tree 有许多种类型,包括AVL-tree、RB-tree、AA—tree,其中最广泛运用于STL的是RB-tree。
8、二叉搜索树 binary search tree
可以提供对数时间的元素插入和访问。二叉搜索树的节点防止规则是:任何节点的键值一定大于其左子树的每一个节点的键值,并小于右子树的每一个节点的键值。因此,从根节点一直往左走,直至无路可寻,即得到最小元素,从根节点一直往右走,直至无路可走,就得到最大元素。
二叉平衡搜索树 balanced binary search tree,所谓树的平衡与否,并没有一个绝对的度量标准。平衡大致的意义是:没有任何一个节点过深(深度过大)。不同的平衡条件,造就出不同的效率的表现,以及不同的实现复杂度。如:AVL-tree、RB-tree、AA-tree,均可以实现平衡二叉树,他们都比一般的二叉搜索树复杂,因此插入节点和删除结点的平均时间也比较长,但是它们可以避免极难应付的最坏情况(高度不平衡),而且由于它们总能够保持平衡状态,所以元素的访问搜索时间平局而言比较少,一般而言可以节约时间省25%左右。
9、AVL-tree
AVL-tree是一个“加上了额外平衡条件”的二叉搜索树、平衡条件的建立是为了确保整棵树的深度为O(logN)。直观上的最佳平衡条件是每个节点的左右子树有着相同的高度,但这未免有些严苛,我们很难保证元素达到这样的平均平衡。AVL-tree退而求其次,要求任何节点的左右子树高度相差最多不过1。这是一个较弱的条件,但是能够保证对数深度的平衡状态。
10、红黑树:http://www.cnblogs.com/newpanderking/p/3870579.html
红黑树的平衡性没有AVL-tree高,要求比较弱,但是红黑树通常也能够达到良好的平衡状态,根据经验得到,红黑树和AVL-tree的搜寻效率几乎相等。
posted on 2014-08-03 16:53 NewPanderKing 阅读(370) 评论(0) 编辑 收藏 举报