红黑树的插入——层层历历在目

插入

  • 接前面
    • 红黑树的删除
    • 234树的层序打印
    • 代码
  • 插入前
  • 《算法导论》只有四种情形
    • 插入3结点情形
    • 情形一:直接插入
    • 情形二:反色递归调用
    • 情形三四
  • 验证
    • 一个个地插入
    • 升层
    • 加一减一插入
    • 批量插入
  • 后话

接前面

红黑树的删除

会对红黑树和234树有基本的了解。
重点234树与红黑树同构。

234树的层序打印

打印红黑树和对应的234树,展现插入操作升层的情形,以验证理论知识,更好地理解红黑树。

代码

实现了红黑树的插入和删除操作。

插入前

找到插入位置的叶子结点,只有父结点是红色的情形才要调整。情形并不多,但是插满时要向上调整,怎么升层操作呢?《算法导论》第13章有讲RB-INSERT-FIXUP,跟删除调整一样只有四种情形。

《算法导论》只有四种情形

【插入结点的父结点为一左子结点】

算法导论4情形调整
case0:在黑结点下插入直接插入
case1:在红结点下插入,且该结点有红色兄弟结点 (插入结点的叔结点)原4-结点的3个结点反色
case2: 插入3结点,「<」形在红结点下插入,且该结点无红色兄弟结点 (插入结点的叔结点)插入结点为右子结点:下段左旋>反色>上段右旋
case3:插入3结点,「/」形在红结点下插入,且该结点无红色兄弟结点 (插入结点的叔结点)插入结点为左子结点:反色>上段右旋

2结点,直接插入。4结点,直接反色向上调整,递归调用调整自己。

插入3结点情形

直接常数时间复杂度内调整,不用向上调整。列出了所有情形。
在这里插入图片描述

情形一:直接插入

添加新结点的操作在插入前完成的,这里直接判断返回就可以了。向上调整走到这里的时候也不用添加新结点,就是直接返回。

if p.color == BLACK:
    return

剩下三种情形,要在有父接点的情形。而且还有镜像的代码,像前面图表是插入结点的父结点为左子结点,还有右右结点的情形。它们都要写在这个 i f if if下面。

if p.parent:
    g = p.parent
    if g.right == p:# 结点w为x叔结点
        w = g.left
    else:
        w = g.right

情形二:反色递归调用

if w and w.color == RED:#调用回来
    w.PAINT(BLACK)
    p.PAINT(BLACK)
    g.PAINT(RED)
    if g.parent:#回溯后,不能转到下面的情况调整。
        p = g.parent
        x = g
        self.INSERT_FIXUP(p,x)
    else:#到根了
        g.PAINT(BLACK)
    return

情形三四

w = = N o n e o r w . c o l o r = = B L A C K w == None or w.color == BLACK w==Noneorw.color==BLACK,空结点也是黑色的。跟情形二是互斥的,不能走了上面再走这里,要用 e l i f elif elif。图片和表格里说的反色有时是对234树来说的,所以 x x x旋转作根后3结点的三个 k e y key key都要反色,比如 x . l e f t . P A I N T ( R E D ) x.left.PAINT(RED) x.left.PAINT(RED)

#3.插入3结点,「<」形;在红结点下插入,无红叔,插入结点为右子结点: 下段左旋>反色>上段右旋
elif w == None or w.color == BLACK:
    if p == g.left:#插入结点的父结点为一左子结点
        if x == p.right:
            self.LEFT_ROTATE(p)
            x.left.PAINT(RED)#x作根后3结点的三个key都要反色
            x.PAINT(BLACK)
            g.PAINT(RED)
            self.RIGHT_ROTATE(g)
        else:
            #4.插入3结点,「/」形,插入结点为左子结点: 反色>上段右旋
            p.PAINT(BLACK)
            g.PAINT(RED)
            self.RIGHT_ROTATE(g)
    else:#插入结点的父结点为一右子结点
        if x == p.left:#插入结点插入后为左子结点:下段右旋>反色>上段左旋
            self.RIGHT_ROTATE(p)
            x.right.PAINT(RED)#x作根后3结点的三个key都要反色
            x.PAINT(BLACK)
            g.PAINT(RED)
            self.LEFT_ROTATE(g)
        else:
            #插入结点插入后为右子结点: 反色>上段左旋
            p.PAINT(BLACK)
            g.PAINT(RED)
            self.LEFT_ROTATE(g)

验证

还是用《算法与数据结构》平衡二叉排序树的列子。插入【27, 73, 10, 5, 18, 41, 99, 51, 25】

一个个地插入

>>> rb.INSERT(27)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(2, [27])
>>> rb.INSERT(73)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(3, [27, 73])
>>> rb.INSERT(10)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, red) (73, red) 
>>> rb.INSERT(5)
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, black) (73, black) (5, red) 
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])

升层

上面插入五的时候升层了。上面把升层前后红黑树的层序遍历也打印出来了。

>>> rb.INSERT(18)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(2, [18])
>>> rb.INSERT(41)
>>> rb.INSERT(99)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(2, [18])
结点类型和值(2, [41])
结点类型和值(2, [99])
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, black) (73, black) (5, red) (18, red) (41, red) (99, red) 
>>> rb.INSERT(51)
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, black) (73, red) (5, red) (18, red) (41, black) (99, black) (51, red) 
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(3, [27, 73])
结点类型和值(4, [5, 10, 18])
结点类型和值(3, [41, 51])
结点类型和值(2, [99])
>>> rb.INSERT(25)
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, red) (73, red) (5, black) (18, black) (41, black) (99, black) (25, red) (51, red) 
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(3, [41, 51])
结点类型和值(2, [99])
>>> rb.INSERT(28)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(4, [28, 41, 51])
结点类型和值(2, [99])

加一减一插入

都插入进去也才两层。一个数加一,一个数再减一地插入吧。【28, 72, 11, 4…】
插入到72的时候升三层了,但是忘记打印升层前后的红黑树了。删除刚插入的接点,可以回到插入前的状态吗?应该回到之前的状态吗?这就是后话了。

>>> rb.INSERT(72)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(3, [28, 41])
结点类型和值(2, [99])
结点类型和值(3, [51, 72])
>>> rb.DELETE(72)
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(3, [28, 41])
结点类型和值(2, [99])
结点类型和值(2, [51])

批量插入

从上面的打印情况看,是没有回到之前的状态的。那把28加到列表最后一起插入吧。

a = [27, 73, 10, 5, 18, 41, 99, 51, 25, 28]
rb = RBT()
for v in a:
    rb.INSERT(v)
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, red) (73, red) (5, black) (18, black) (41, black) (99, black) (25, red) (28, red) (51, red) 
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(4, [28, 41, 51])
结点类型和值(2, [99])
>>> rb.INSERT(72)
>>> rb.breadth_fisrt_level_traversal(rb.root)
(27, black) (10, black) (73, black) (5, black) (18, black) (41, red) (99, black) (25, red) (28, black) (51, black) (72, red) 
>>> t=tree234(rb.root)
>>> t.print()
结点类型和值(4, [10, 27, 73])
结点类型和值(2, [5])
结点类型和值(3, [18, 25])
结点类型和值(3, [28, 41])
结点类型和值(2, [99])
结点类型和值(3, [51, 72])

插入72后升到了三层的234树。前面提到的1到50插入后是6层的。

后话

红黑树是红一层黑一层的吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值