红黑树学习
最近在研究map和unordered_map,接触到了红黑树这个东西..大学一直想学而始终没学,所以学习学习 感谢作者
1,http://blog.csdn.net/v_JULY_v/article/details/6105630
2,http://blog.csdn.net/v_JULY_v/article/details/6109153
3,http://blog.csdn.net/v_JULY_v/archive/2011/01/03/6114226.aspx
4,http://blog.csdn.net/v_JULY_v/archive/2011/01/09/6124989.aspx
5,http://blog.csdn.net/v_JULY_v/article/details/6284050
6,http://blog.csdn.net/v_JULY_v/article/details/6285620
一、红黑树的介绍
红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树,作为一棵二叉查找树,满足二叉查找树的一般性质。下面,来了解下 二叉查找树的一般性质。
二叉查找树
二叉查找树,也称有序二叉树(ordered binary tree),或已排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树。
- 没有键值相等的节点(no duplicate nodes)。
因为一棵由n个结点随机构造的二叉查找树的高度为log2n,所以顺理成章,二叉查找树的一般操作的执行时间为O(log2n)。但二叉查找树若退化成了一棵具有n个结点的线性链后,则这些操作最坏情况运行时间为O(n)。(当二叉查找树是平衡二叉查找树[AVL]的时候时间复杂度是O(log2n),而斜树的时候是O(n)
二叉平衡树: 它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法 平衡二叉树的常用算法有红黑树、AVL、Treap等。 |
红黑树虽然本质上是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
但它是如何保证一棵n个结点的红黑树的高度始终保持在logn的呢?这就引出了红黑树的5个性质:
- 每个结点要么是红的要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
- 如果一个结点是红的,那么它的两个儿子都是黑的。
- 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
正是红黑树的这5条性质,使一棵n个结点的红黑树始终保持了logn的高度,从而也就解释了上面所说的“红黑树的查找、插入、删除的时间复杂度最坏为O(log n)”这一结论成立的原因。
(注:上述第3、5点性质中所说的NULL结点,包括wikipedia.算法导论上所认为的叶子结点即为树尾端的NIL指针,或者说NULL结点。然百度百科以及网上一些其它博文直接说的叶结点,则易引起误会,因,此叶结点非子结点)
如下图所示,即是一颗红黑树(下图引自wikipedia:http://t.cn/hgvH1l):
此图忽略了叶子和根部的父结点。同时,上文中我们所说的 "叶结点" 或"NULL结点",如上图所示,它不包含数据而只充当树在此结束的指示,这些节点在绘图中经常被省略,望看到此文后的读者朋友注意。(上图 NIL是NULL也就是最后的叶节点只不过这个是实现为NULL值,还是一个假节点,看你喜欢了)
二、树的旋转知识
当在对红黑树进行插入和删除等操作时,对树做了修改可能会破坏红黑树的性质。为了继续保持红黑树的性质,可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即通过修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后继续保持它的性质或平衡的目的。
树的旋转分为左旋和右旋,下面借助图来介绍一下左旋和右旋这两种操作。
1.左旋
如上图所示,当在某个结点pivot上,做左旋操作时,我们假设它的右孩子y不是NIL[T],pivot可以为任何不是NIL[T]的左子结点。左旋以pivot到Y之间的链为“支轴”进行,它使Y成为该子树的新根,而Y的左孩子b则成为pivot的右孩子。
1 LeftRoate(T, pivot) 2 y <- pivot.right //定义y: y是x的右孩子 3 pivot.right <- y.left //y的左孩子成为x的右孩子 4 5 if y.left ≠ T.nil 6 y.left.paret <- pivot 7 y.paret <- x.paret //x的父节点成为y的父节点 8 if pivot.paret = T.nil 9 then T.root <-- y 10 else if pivot = pivot.paret.left 11 then pivot.paret.right <-- y 12 else pivot.paret.right <-- y 13 y.left <-- pivot //x作为y的左孩子 14 pivot.paret <-- y
2.右旋 右旋与左旋差不多,再次不做详细介绍
树在经过左旋右旋之后,树的搜索性质保持不变,但树的红黑性质则被破坏了,所以,红黑树插入和删除数据后,需要利用旋转与颜色重涂来重新恢复树的红黑性质。
三、红黑树的插入
要真正理解红黑树的插入,还得先理解二叉查找树的插入。磨刀不误砍柴工,咱们再来了解一下二叉查找树的插入和红黑树的插入。
如果要在二叉查找树中插入一个结点,首先要查找到结点要插入的位置,然后进行插入。假设插入的结点为z的话,插入的伪代码如下:
1 TREE-INSERT(T, z) 2 y ← NIL 3 x ←T.root 4 while x ≠ NIL 5 do y ← x 6 if z.key < x.key 7 then x ← x.left 8 else x ← x.right 9 z.p ← y 10 if y == NIL 11 then T.root ← z 12 else if z.key < y.key 13 then y.left ← z 14 else y.right ← z