红黑树删除之向上调整

bug

从图来看,向上调整了一层,2的父结点4也做了调整。

删除0

正确的删除

正确删除0后应该是这样的。 0, 17, 16, 10, 11, 12, 1, 21, 18, 4, 19, 8, 14, 3, 2, 7, 20, 15, 13, 9, 6, 522个随机数构成的红黑树删除0。

删除0后

错误的删除

调试代码时错误的情况。2作为1的左子树上也不符合二叉查找树的要求。

删除0错误的情况

删除之前

删除之前

0是黑色结点,删除后会破坏红黑树的平衡。是情形3:兄弟w为黑色,且其孩子左红右空。调整后转成情形4再调整一次就恢复平衡,不用再向上调整。正好是红黑树的删除图例的情形3。

情形四修正

#4.w为黑色,且其孩子左黑右红或左红右红。调试代码是这里出了问题。黑有时也是空的意思。要添加左空右红的情况。

原代码

复制代码
if w and ((w.left and w.left.color == BLACK and w.right and w.right.color == RED )

改正后

镜像的代码也对应修改。

复制代码
if w and (((w.left == None or w.left and w.left.color == BLACK )and w.right and w.right.color == RED )

用例

a = 0, 17, 16, 10, 11, 12, 1, 21, 18, 4, 19, 8, 14, 3, 2, 7, 20, 15, 13, 9, 6, 5

22个数据递增和递减构造的图都是7层,随机数据构造的图是6层。

复制代码
#22递增的图,递减的图都是7层,随机图6层,
#for v in range(22):
#for v in range(21,-1,-1): #22递增的图
#a = [i for i in range(22)]
#random.shuffle(a)
#print(a)
a = [0, 17, 16, 10, 11, 12, 1, 21, 18, 4, 19, 8, 14, 3, 2, 7, 20, 15, 13, 9, 6, 5]
for v in a:
    rb.INSERT(v)  

本来想看看删除递归向上调整的情况,一删除0,代码就有问题了。画出图形,用随机数测试还是很有帮助的。

删除1

从前面第一张图"删除0后"可以看出,此时删除1是情形2:兄弟w为黑色,且其孩子为空 。需要向上调整,是怎么调整的呢?

这一删问题更大了,还有好多bug,这里略去了很多图。具体参照红黑树的删除修改。

主要修改

  • 1、 反色。要判断之前颜色再设置新颜色。
py 复制代码
                if w.left.color == RED:
                    w.left.PAINT(BLACK)
                else:
                    w.left.PAINT(RED)
  • 2、置 w 与 x . p w与 x. p w与x.p同色。 之前试的例子太少,自以为是地写成了黑色。
  • 3、黑有时是表示NIL结点。

修改前

py 复制代码
        #2.w为黑色或空,且其孩子左黑右黑        
        if w == None or (w.left and w.left.color == BLACK and w.right and w.right.color == BLACK)or(w.left is None and w.right is None):
            if w:w.PAINT(RED)
            if p.parent:            
                p.PAINT(BLACK)
                if p == p.left:
                    w = p.right
                    p = p.parent
                else:
                    w = p.right
                    p = p.parent
                self.DELETE_FIXUP(p,w) 


        #4.w为黑色,且其孩子左黑右红或左红右红
        if w and (((w.left == None or w.left and w.left.color == BLACK ) and w.right and w.right.color == RED )
            or (w.left and w.left.color == RED and w.right and w.right.color == RED)):
            p.PAINT(BLACK)
            if w == p.right:
                self.LEFT_ROTATE(p)
                w.right.PAINT(BLACK)
            elif w == p.left:
                self.RIGHT_ROTATE(p)
                w.left.PAINT(BLACK)
        #镜像情况                
        elif w and (((w.right == None or w.right and w.right.color == BLACK ) and w.left and w.left.color == RED )
            or (w.right and w.right.color == RED and w.left and w.left.color == RED)):
            p.PAINT(BLACK)
            if w == p.left:
                self.RIGHT_ROTATE(p)
                w.left.PAINT(BLACK)
            elif w == p.right:
                self.LEFT_ROTATE(p)
                w.right.PAINT(BLACK) 

修改后

py 复制代码
        #2.w为黑色或空,且其孩子左黑右黑        
        if w == None or (w.left and w.left.color == BLACK and w.right and w.right.color == BLACK)or(w.left is None and w.right is None):
            if w:w.PAINT(RED)
            if p.parent:            
                if p == p.parent.left:
                    w = p.parent.right
                    p = p.parent
                else:
                    w = p.parent.right
                    p = p.parent
                self.DELETE_FIXUP(p,w)
        #3.w为黑色,且其孩子左红右黑
        if w and w.left and w.left.color == RED and (w.right == None or w.right and w.right.color == BLACK):
            w.left.PAINT(BLACK)
            w.PAINT(RED)
            self.RIGHT_ROTATE(w)
            w = p.right
        #镜像情况
        elif w and w.right and w.right.color == RED and (w.left == None or w.left and w.left.color == BLACK):
            w.right.PAINT(BLACK)
            w.PAINT(RED)
            self.LEFT_ROTATE(w)
            w = p.left 
        #4.w为黑色,且其孩子左黑右红或左红右红
        if w and (((w.left == None or w.left and w.left.color == BLACK ) and w.right and w.right.color == RED )
            or (w.left and w.left.color == RED and w.right and w.right.color == RED)):
            w.PAINT(p.color)
            p.PAINT(BLACK)
            if w == p.right:
                self.LEFT_ROTATE(p)
                if w.right.color == RED:
                    w.right.PAINT(BLACK)
                else:
                    w.right.PAINT(RED)
            elif w == p.left:
                self.RIGHT_ROTATE(p)
                if w.left.color == RED:
                    w.left.PAINT(BLACK)
                else:
                    w.left.PAINT(RED)
        #镜像情况                
        elif w and (((w.right == None or w.right and w.right.color == BLACK ) and w.left and w.left.color == RED )
            or (w.right and w.right.color == RED and w.left and w.left.color == RED)):
            w.PAINT(p.color)
            p.PAINT(BLACK)
            if w == p.left:
                self.RIGHT_ROTATE(p)
                if w.left.color == RED:
                    w.left.PAINT(BLACK)
                else:
                    w.left.PAINT(RED)
            elif w == p.right:
                self.LEFT_ROTATE(p)
                if w.right.color == RED:
                    w.right.PAINT(BLACK)
                else:
                    w.right.PAINT(RED)  

还是错

删除调整修正后删除1的图

3在2的左支,不符合红黑树,也不符合二叉排序树。

调试

不应该啊,已接近崩溃,又做了如下测试。

复制代码
rb.DELETE(1)
rb.draw(rb.root)

rb.root.left.left.left.left.val
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    rb.root.left.left.left.left.val
AttributeError: 'NoneType' object has no attribute 'val'
rb.root.left.left.left.right.val
3

2的左支没有3,它在右支。不会是画图代码有问题吧!加个search方法直接定位结点,这就是后话了。

修正

之前编码时,先写的只有左支的情况,是入队左和NIL。再抄到只有右支的情况,写成了先入队右再入队NIL。

py 复制代码
            elif q.right:
                temp.append(RBTnode(-1))
                temp.append(q.right)
                endlevel = level + 1
#之前写的是:
            elif q.right:
                temp.append(q.right)
                temp.append(RBTnode(-1))
                endlevel = level + 1
代码里还有错吗?

从图来看,向上调整了一层,2的父结点4也做了调整。也许一直向上调整的情况没有那么容易碰到。调试情况如下:

DELETE_FIXUP只嵌套调用了一次

重点

失业了,求职中!能有口饭吃将不胜感激。

相关推荐
金銀銅鐵5 小时前
[Python] 基于欧几里得算法,实现分数约分计算器
python·数学
Lyn_Li7 小时前
Kaggle Top 5 | 198只股票、200条数据的金融预测——BattleFin高分方案从零复现
python·kaggle·比赛复盘·金融预测
小九九的爸爸11 小时前
前端想要入门Agent开发,要具备哪些Python基础?
python·agent·ai编程
阿耶同学12 小时前
手把手教你用 LangGraph 搭建三层嵌套 Agent 架构
python·程序员
花酒锄作田1 天前
Pydantic校验配置文件
python
hboot1 天前
AI工程师第四课 - 深度学习入门
pytorch·python·神经网络
ZhengEnCi2 天前
P2M-Matplotlib折线图完全指南-从数据可视化到趋势分析的Python绘图利器
python·matlab·数据可视化
ZhengEnCi2 天前
P2L-Matplotlib饼图完全指南-从数据可视化到图表定制的Python绘图利器
python·matlab
曲幽2 天前
你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南
python·fastapi·web·graphql·route·cors·rest·strawberry
用户8358086187912 天前
基于 Self-RAG 与列表级重排序的进阶 RAG 系统设计与实现
python