<![endif]--> <![endif]-->
二叉搜索树
二叉搜索树 (binary search tree) ,可提供对数时间 (10garithmictime)3 的元素插入和访问。二叉搜索树的节点放置规则是:任何节点的键值一定大干其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。因此,从根节点一直往左走,直至无左路可走,即得最小元素:从根节点一直往右走,直至无右路可走,即得最大元素。
查找
图 5-4 所示的就是一棵二叉搜索树。要在一棵二叉搜索树中找出最大元素或最小元素,是一件极简单的事:就像上述所言,一直往左走或一直往右走即是。
插入
插人新元素时,可从根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插人点。
删除
欲删除旧节点 A ,情况可分两种。如果 A 只有一个子节点,我们就直接将 A 的子节点连至 A 的父节点,并将 A 删除。如果 A 有两个子节点,我们就以右子树内的最小节点取代 A 。注意,右子树的最小节点极易获得:从右子节点开始 ( 视为右子树的根节点 ) ,一直向左走至底即是。
平衡二叉搜索树
所谓树形平衡与否,并没有一个绝对的测量标准。“平衡”的大致意义是:没有任何一个节点过深 ( 深度过大 ) 。不同的平衡条件,造就出不同酌效率表现,以及不同的实现复杂度。 (每一种平衡树都有不同于其他平衡树的平衡条件,但是所有的平衡树都满足二叉搜索树的条件) 。
AVL tree(Adelson-Velskii-Landistree)
AVL tree 是一个“加上了额外平衡条件”的二叉搜索树。其平衡条件的建立是为了确保整棵树的深度为O(logN) 。直观上的最佳平衡条件是每个节点的左右子树有着相同的高度,但这未免太过严苛。 AVL tree 于是退而求其次,其平衡条件为:任何节点的左右子树高度相差最多 1 。这是一个较弱的条件,但仍能够保证“对数深度 (logarithmic depth) ”平衡状态。
由于只有“插入点至根节点”路径上的各节点可能改变平衡状态,因此,只要调整其中最深的那个节点,便可使整棵树重新获得平
假设该最深节点为 X ,由于节点最多拥有两个子节点,而所谓“平衡被破坏”意味着 X 的左右两棵子树的高度相差 2 ,因此我们可以轻易将情况分为四种:
1 .插人点位于 X 的左子节点的左子树——左左。
2 .插入点位于 X 的左子节点的右子树——左右。
3 .插入点位于 X 的右子节点的左子树——右左。
4 .插入点位于 X 的右子节点的右子树——右右。
情况 1 , 4 彼此对称,称为外侧 (outside) 插入,可以采用单旋转操作 (singlerotation) 调整解决。情况 2 , 3 彼此对称,称为内侧 (inside) 插入,可以采用双旋转操作 (doublerotation) 调整解决。
<![endif]-->
RB tree( 红黑树 )
RB — tree 不仅是一个二叉搜索树,而且必须满足以下规则 ( 即平衡条件 ) :
1. 每个节点不是红色就是黑色
2 .根节点为黑色。
3 .如果节点为红,其子节点必须为黑。
4 .任一节点至 NULL( 树尾端 ) 的任何路径,所含之黑节点数必须相同。
根据规则 4 ,新增节点必须为红:根据规则 3 ,新增节点之父节点必须为黑。当新节点根据二叉搜索树的规则到达其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。
假设新节点为 X ,其父节点为 P ,祖父节点为 G ,伯父节点 ( 父节点之兄弟节点 ) 为 S ,曾祖父节点为 GG 。现在,根据二叉搜索树的规则,新节点 X 必为叶节点。根据红黑树规则 4 , X 必为红。所以,所有需要调整树形(或者颜色)的情况中, P 必定为红 ( 如此违反了规则 3 ,才会调整树形 ) 。根据规则 3 可知父子节点不能同时为红,所以 G 必为黑 ( 因为未插入新节点 X 之前该树为 RB — tree ,必须遵循规则 3) 。 需要调整树形的情况仅仅与 X 的插入位置及外围节点 (S 和 GG) 的颜色有关 ( 由,前面可知 X,P,G 的颜色已经推定 ) ,故有以下四种考虑:
状况 1: S 为黑且 X 为外侧插入。对此情况,我们先对 P,G 做一次单旋转再更改 P,G 颜色,即可重新满足红黑树的规则 3 。如图:
<![endif]-->
注意 ,此时可能产生不乎衡状态 ( 高度相差 l 以上 ) 。例如图中的 A 和 B 为 null , D 或 E 不为 null 。这倒没关系,因为 RB — tree 的平衡性本来就比 AVL — tree 弱。然而 RB — tree 通常能够导致良好的平衡状态:是的,经验告诉我们, RB — tree 的搜寻平均效率和 AVL — tree 几乎相等。
状况 2 : S 为黑且 X 为内侧插入。对此情况,我们必须先对 P , X 做一次单旋转并更改 G , x 颜色,再将结果对 G 做一次单旋转,即可再次满足红黑树规则 3 。
<![endif]-->
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
《 STL 源码剖析》原文中的状况 3,4
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
状况 3 : S 为红且 X 为外侧插入。对此情况,先对 P 和 G 做一次单旋转,并改变 X 的颜色:此时如果 GG 为黑,一切搞定。如下图,但如果 GG 为红,则问题就比较大些,见状况 4 .
<![endif]-->
状况 4 : S 为红且 X 为外侧插入。对此情况,先对 P 和 G 做一次单旋转,并改变 X 的颜色、此时如果 GG 亦为红,还得持续往上做,直到不再有父子连续为红的情况。
<![endif]-->
备注:状况 3 和 4 为 STL 源码剖析原文中给出的情况。观察源码便知,此处与源码不符。参考《勘误 <STL 源码剖析 > 》,作者做出的解释十分牵强,并不具有任何说服力。《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
自己理解的状况 3,4
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
状况 3 : S 为红且 X 为外侧插入,或者为内侧插入。对此情况,改变 P,G,S 颜色,此时如果 GG 为黑,一切搞定。如果 GG 为红,见状况 4 。
<![endif]-->
状况 4 : S 为红且 X 为外侧插入,或者为内侧插入。对此情况,改变 P,G,S 颜色,此时如果 GG 为红持续往上做,直到不再有父子连续为红的情况。
<![endif]-->
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《