STL源码剖析之关联式容器基础知识
2011-06-12 10:11 Aga.J 阅读(359) 评论(0) 编辑 收藏 举报54 标准的STL关联式容器有 set (multiset)和 map(multimap),他们底层的机制都是RB-tree即红黑树来实现。还有其他 hash_table, hash_set,hash_map,hash_multiset,hash_multimap(他们的底层都是以hash_table来实现)
RB-tree
Set (RB_SET)
Map (RB_MAP)
Multiset (RB_MULTISET)
Multimap (RB_MULTIMAP)
Hashtable
Hash_set
Hash_map
Hash_multiset
Hash_mutimap
关联式容器,概念上类似“关联式数据库”每个元素都有一个key和value、
当元素插入到关联式容器中时,容器内部结构RB-tree(平衡二叉树,有较好的搜索效率,还有其他类型如AVL,RB,AA)或者hash-table就会按照key的大小,用某种规则将元素放在适当的位置。
55 平衡二叉树
不同的平衡条件,造就出不同的效率表现,以及不同的实现复杂度,AVL / RB / AA都是一种平衡二叉树,因为维护平衡,所以插入和删除节点的平均时间长了,但是可以避免不平衡的情况,所以元素的搜寻访问时间短了!
最直观的平衡条件是整棵树的深度为logN,并要求每个节点的左右子树有相同的高度(递归定义的结果就是该树的叶子节点都在同一层上!)
而AVL- Adelson-Velskii-Landis tree(AVL tree)树则允许任何节点的左右子树的高度相差1,(递归定义的结果就是该树的叶子节点可以在最后一层和倒数第二层)
56 AVL树
一旦插入节点后,AVL树不平衡,只要调整“插入点到根节点”的那条路径上,平衡状态被破坏的所有节点中 【最深】的那个(怎么找到这个最深的?只要逐步向上溯,得到当前节点的父节点的左右子树的深度相差大于一),就可以使得整棵树重新获得到平衡
那么有以下四种情况
1 插入点位于X 的左子节点的左子树 ----左左 (外侧) //不管是在左子树的什么位置上插入
2 插入点位于X的左子节点的右子树 -- 左右 (内侧)
3 插入点位于X的右子节点的左子树 右左 (内侧)
4 插入点位于X的右子节点的右子树 右右 (外侧)
也就是说正确的找到X节点就显得十分重要了!
在外侧插入—也就是在 完成外侧插入后,某个顶点成为第一个违反平衡条件,是下面这种状态(违反点为k2)
也就是情况【1】【4】
1 插入点位于X 的左子节点的左子树 ----左左 (外侧)
4 插入点位于X的右子节点的右子树 (内侧)
插入11后,使得A子树(k1节点的左子树)成长了一层,但是这样并不会让k1树不平衡,但是A子树的深度却比C子树的深度多2,也就使得k2的左子树的高度为3,k2的右子树的高度为1,破坏了平衡。而且这种情况下,B子树不可能和A子树同层,不然的话早就不平横了,同时B子树也不可能和C子树同层,不然的话第一个违反平衡条件的就是k1而不是k2了。
我们希望A子树向上提,而C子树下降一层,可以从图里这样想象,只需要把K1向上提,而K2自然下滑,并且把B子树挂到K2的左侧,就完成了这个过程(因为本身AVL是一颗平衡二叉树,子树有一定的大小关系,所以这样操作可以得到正确的结果)右右插入的时候同理!
在内侧插入 – 也就是在情况【2】,【3】
2 插入点位于X的左子节点的右子树 -- 左右 (内侧)
3 插入点位于X的右子节点的左子树 (外侧)
这次我们没办法对k1和k3只进行一次旋转,使得k1为根节点来完成平衡。我们可以考虑用k2节点来作为平衡树的根,这就需要两次旋转
对照着上述的旋转方法,给出基本的旋转操作(暂时不考虑其他节点为空的情况)
Void singleRotation( Node* n,bool left) //n节点表示当前节点违反了平衡条件
{
Node *newHead;
If(left==TRUE) //新插入的节点在n的左子树
左左,进行右上提拉节点,注意只适合左子节点的左子树,如果是左子节点的右子树,则需//要两次旋转,意思就是只适合外侧
{
newHead = n->left; //在这里指k2
if(newHead->right!=null)
n->left=newHead->right;
newHead->right=n;
n=newHead;
}
Else //右右,进行左上提拉节点
{
newHead = n->right;
if(newHead->left!=null)
n->right= newHead->left;
newHead->left = n;
n= newHead;
}
}
Void doubleRotation(Node* n, bool left)
{
If(left == TRUE) //新插入的节点在n的左子树
//左右,注意只适合左子节点的右子树,如果是左子节点的右子树,值需要一次旋转,内侧旋转
{
singleRotation(n->left,false); //第一次旋转,对左子节点树进行一次左旋,如下图所示
singleRotation(n,true); //第二次旋转,然后对该节点进行一次右旋
}
Else
{
singleRotation(n->right,true);
singleRotation(n,false);
}
}
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。