Introduce to algorithm--------pseudo code to C/C++ code(chapter 13)

红黑树

虽然二叉树在理想情况下的锁定胡常见操作的时间复杂度均为lg(n), 但在最坏情况下,二叉树的平衡性将被打破,导致操作的时间复杂度上升。由于在实际应用中不能保证二叉树的平衡性, 想来红黑树的出现也不足为奇了。

红黑树在一定程度上确保了树结构的平衡性,但也是因此,红黑树的构建和相关的操作相对于二叉树而言要复杂的多。

以下是红黑树性质在算法导论上的描述:

树中每个结点包含5个属性:color、key、left、right和p。

通过对任何一条从跟到叶子的简单路径上各个节点的颜色进行约束,红黑树可以确保没有一条路径会比其他路径长出2倍,因而是近似平衡的。

一颗红黑树是满足下面红黑性质的二叉搜索树:

  • 1.每个结点是红色的,或是黑色的。
  • 2.根结点是黑色的。
  • 3.每个叶结点(NIL)是黑色的。
  • 4.如果一个结点是红色的,则它的两个子结点都是黑色的。
  • 5.对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

结点增加了color属性来平衡二叉树。为了处理红黑树代码中的边界条件,算法导论中使用哨兵nil来代替NULL,即叶子结点的子结点和根结点的父结点为哨兵。nil的颜色值为Black,其他属性可为任何值。见下图:
nil

rotate

如果沿用二叉树的insert和delete操作,可能会破坏红黑树的上述性质,所以在红黑树上的操作需要rotate函数来维护其性质。旋转分为左旋和右旋,其操作和结果是相对称的,改变的是结点的颜色和指针结构。
rotate

insert

红黑树的插入函数在二叉树的插入函数基础上进行了修改,它将待插入的结点着色为红色,并调用辅助函数RB_Insert_Fixup()来对结点重新着色并旋转,以保持红黑树的性质,所以重要的是这个辅助函数.

便于理解函数,在这里需要分析按上述条件插入结点后,可能会违反的红黑树性质。下面这段原话比较重要:

“在调用RB_INSERT_FIXUP操作时,哪些红黑性质可能被破坏呢?性质1和性质3继续成立,因为新插入的红结点的两个子结点都是哨兵T.nil。性质5,即从一个指定节点开始的每条简单路径上的黑结点个数都是相等的,也会成立,因为结点z代替了黑哨兵,并且结点z本身是哨兵孩子的结点。这样看来,仅可能被破坏的性质就是性质2和性质4,即根结点需要为黑色及一个红结点不能有红孩子。这连个性质可能被破坏就是因为z被着为红色。如果z是根结点,则破坏了性质2 ;如果z的父结点是红结点,则破坏了性质4。”

函数伪代码如下图:
function

可见,函数由一个主循环构成,分三种情况,分别对应上图中的case1、case2、case3。算法导论中用循环不变式来证明:

第1-15行中的while循环在每次迭代的开始保持下列3个部分的不变式:

a.结点z是红结点。

b.如果z.p是根结点,则z.p是黑结点.

c.如果有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2,或是性质4.如果性质2被破坏,其原因为z是根结点且是红结点。如果性质4被破坏,其原因为z和z.p都是红结点。

上图函数中的代码以处理c情况为重点。

初始化(即循环的每一次迭代前):

a.结点z是(或可认为是)新增的红结点.

b.如果z.p是根结点,那么z.p开始是黑色的。

c.调用该函数前,性质1、3、5成立。

终止:

a.不再有新增结点,成立。

b.函数16将根结点着色为黑色,没有违反性质2。成立。

c.由主循环条件可知,循环终止是因为z.p为黑色(如果z是根结点,z.p为黑色哨兵nil),所以没有违反性质4。成立。

保持:

a.每次循环迭代后,都将指针指向调整后的新结点,且该节点被着色为红色。
b.循环直到根结点结束,循环过程中不改变根结点颜色,循环结束时,无论根结点是否改变,都将被着色为黑色。
c.已保证性质1、2、3、5成立。而循环的每次迭代将调整存在的不平衡且沿树向上,直至遇到根结点。所以性质4也保持成立。

函数代码分三种情况,分别调整结点位置,使其平衡.详见算法导论.

delete

同样的,红黑树的删除操作也由二叉树中的删除操作修改而来,因为大部分相容。rb_delete函数也需要一个替换函数transplant,但它与二叉树中的替换有所不同,个人感觉也没有什么大不同,就此略过。

以下为rb_delete函数伪代码截图:

rb_delete

嗨,看这个伪代码真是累死我了。要是没有算法导论的描述,看起来真是要累瘫了。
rb_delete函数的代码与二叉树的delete代码在结构上基本相同,但多了y、x、y-original-color三个变量的记录。所以看上去,伪代码量比二叉树的版本多了一倍。

  • y:记录从树中删除的结点或由于树结构调整而移至树内(这个说法不太好)的结点。
  • x:记录y的原始位置。
  • y-original-color:顾名思义,是y结点的原始颜色。

这三个变量非常重要,函数的结尾用这三个函数来判断是否需要调整以平衡红黑树。变量的记录不在此详述,详细请参见算法导论. 这里主要分析红黑性质破坏的情况(摘自算法导论):

如果结点y是黑色,就有可能已经引入了一个或多个红黑性质被破坏的情况,所以在第22行调用RB_DELETE_FIXUP来回复红黑性质。如果y是红色,当y被删除或被移动时,红黑性质任然保持,原因如下:

1.树中的黑高没有变化

2.不存在连个相邻的红结点。因为y在树中占据了z的位置,在考虑到z的颜色,树中y的新位置不可能有两个相邻的红结点。另外,如果y不是z的右孩子,则y的原右孩子代替y。如果y是红色,则x一定是黑色,因此用x代替y不可能使两个红结点相邻。

3.如果y是红色的,就不可能是根结点,所以根结点仍旧是黑色。

如果结点y是黑色的,则会产生3个问题,可以通过调用RB_DELETE_FIXUP进行补救。第一,如果y是原来的根结点,而y的一个红色的孩子成为新的根结点,这就违反了性质2。第二,如果x和x.p是红色的,则违反了性质4。第三,在树中移动y将导致先前包含y的任何简单路径上黑结点个数减少1。因此,y的任何祖先都不满足性质5。改正这一问题的办法是将现在占有y原来位置的结点x视为还有一重(chong)额外的黑色。也就是说,如果将任意包含结点x的简单路径上黑结点个数加1,则在这种假设下,性质5成立。当将黑结点y删除或移动时,将其黑色“下推”给结点x。现在问题变为结点x可能既不是红色,又不是黑色,从而违反了性质1。现在的结点x是双重黑色或红黑色,这就分别给包含x的简单路径上黑节点贡献了2或1。x的color属性仍然是RED(如果x是红黑色的)或者BLACK(如果x是双重黑色的)。换句话说,结点额外的黑色是针对x结点的,而不是反映在它的color属性上的。

以下是RB_DELETE_FIXUP函数伪代码截图:

delete_fixup
如图所示,函数由一个主循环构成。当x结点是x父结点的左结点时,分为四种情况,而情况2情况4是消除x结点额外黑色的唯一途径;当x结点是x父结点的右结点的情况与左结点的情况对称。

1~22行循环的主要目的是将额外的黑色沿树向上,直到:

  • 1.x指向红黑结点,此时在第23行中,将x着色为(单个)黑色。
  • 2.x指向根结点,此时可以简单地“移除”额外的黑色。
  • 3.执行适当的旋转和重新着色,退出循环。

以下为4种情况的截图:

case

仔细观察可知道,4种变换均保持性质5不变,且在case2、case4中消除了x结点多余的黑色属性。表示为w的结点为x的兄弟结点。

  • case1—-当w结点为红色时: 因为调用函数之前性质5是保证成立的,所以当w结点为红色时,其父结点必为黑色。执行情况如图,被转换为case2或case3或case4。
  • case2—-当w结点为黑色,且两个子节点都为黑色时: 将w结点着色为红色,并将x指向x.p,重新进行循环。若由情况1进入情况2,则在测试循环条件后会退出循环,因为x为红色结点,已使所有性质成立。
  • case3—-当w结点为黑色,且w的左结点为红色,右结点为黑色时: 交换w结点和其左结点的颜色,在w结点上执行右旋转。转化为情况4。
  • case4—-当w结点为黑色,且w的右结点为红色: 如图调整着色并在x.p结点上执行左旋。已满足所有红黑树性质,将x指向根结点,退出循环。

对称情况不再描述。

以上即为红黑树要点描述,详见算法导论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值