Python之红黑树

RBTree
方法功能
insert(key)插入值为key的节点
erase(key)删除值为key的节点
get_pre(root)拿到root的前驱节点
left_rotate(root)以root为根进行左旋操作
right_rotate(root)以root为根进行右旋操作
has_red_child(root)判断root子节点是否是红色
insert_maintain(root)对插入操作的回溯进行调整
erase_maintain(root)对删除操作的回溯进行调整

完整代码如下:

NIL = Node(-1, 1, None, None)
NIL.left = NIL
NIL.right = NIL

class Node:
    def __init__(self, key = -1, color = 0, left = NIL, right = NIL):
        self.key = key
        self.color = color
        self.left = left
        self.right = right
        
    def __repr__(self):
        return "[" + str(self.key) + "-" + str(self.color) + ": (" + str(self.left.key) + "-" + str(self.left.color) + ", " + str(self.right.key) + "-" + str(self.right.key) + ")]"     

class RBTree:
    def __init__(self, root = NIL):
        self.root = root
    
    def insert(self, key):
        self.root = self.__insert(self.root, key)
        self.root.color = 1
        return self.root
    
    def __insert(self, root, key):
        if root == NIL:
            return Node(key)
        if root.key == key:
            return root
        if root.key < key:
            root.right = self.__insert(root.right, key)
        else:
            root.left = self.__insert(root.left, key)
        return self.insert_maintain(root)
    
    def insert_maintain(self, root):
        if not self.has_red_child(root):
            return root
        
        if root.left.color == 0 and root.right.color == 0:
            if not self.has_red_child(root.left) and not self.has_red_child(root.right):
                return root
            root.color = 0
            root.left.color = root.right.color = 1
            return root
        
        if root.left.color == 0 and not self.has_red_child(root.left):
            return root
        if root.right.color == 0 and not self.has_red_child(root.right):
            return root
        
        if root.left.color == 0:
            if root.left.right.color == 0:
                root.left = self.left_rotate(root.left)
            root = self.right_rotate(root)
        else:
            if root.right.left.color == 0:
                root.right = self.right_rotate(root.right)
            root = self.left_rotate(root)
        root.color = 0
        root.left.color = root.right.color = 1
        return root
        
    def erase(self, key):
        self.root = self.__erase(self.root, key)
        self.root.color = 1
        return self.root
    
    def __erase(self, root, key):
        if root == NIL:
            return NIL
        if root.key < key:
            root.right = self.__erase(root.right, key)
        elif root.key > key:
            root.left = self.__erase(root.left, key)
        else:
            if root.left == NIL or root.right == NIL:
                tmp = root.left if root.left != NIL else root.right
                tmp.color += root.color
                del root
                return tmp
            tmp = self.get_pre(root)
            root.key = tmp.key
            root.left = self.__erase(root.left, tmp.key)
        return self.erase_maintain(root)
    
    def erase_maintain(self, root):
        if root.left.color != 2 and root.right.color != 2:
            return root
        
        if self.has_red_child(root):
            root.color = 0
            if root.left.color == 0:
                root = self.right_rotate(root)
                root.right = self.erase_maintain(root.right)
            else:
                root = self.left_rotate(root)
                root.left = self.erase_maintain(root.left)
            root.color = 1
            return root
        
        if root.left.color == 2 and not self.has_red_child(root.right) or root.right.color == 2 and not self.has_red_child(root.left):
            root.left.color -= 1
            root.right.color -= 1
            root.color += 1
            return root
        
        if root.left.color == 2:
            root.left.color = 1
            if root.right.right.color != 0:
                root.right.color = 0
                root.right = self.right_rotate(root.right)
                root.right.color = 1
            root = self.left_rotate(root)
            root.color = root.left.color
        else:
            root.right.color = 1
            if root.left.left.color != 0:
                root.left.color = 0
                root.left = self.left_rotate(root.left)
                root.left.color = 1
            root = self.right_rotate(root)
            root.color = root.right.color
        
        root.left.color = root.right.color = 1
        return root
    
    def left_rotate(self, root):
        tmp = root.right
        root.right = tmp.left
        tmp.left = root
        return tmp
    
    def right_rotate(self , root):
        tmp = root.left
        root.left = tmp.right
        tmp.right = root
        return tmp
        
    def get_pre(self, root):
        tmp = root.left
        while tmp.right != NIL:
            tmp = tmp.right
        return tmp
    
    def has_red_child(self, root):
        return root.left.color == 0 or root.right.color == 0
    
    def inorder(self, root):
        if root.left != NIL:
            self.inorder(root.left)
        print(root)
        if root.right != NIL:
            self.inorder(root.right)
        return None

测试代码:

t = RBTree()
for i in range(1, 7):
    t.insert(i)
    if i % 3 == 0:
        t.erase(i - 1)
t.inorder(t.root)

🔴⚫️🌲

😊性质

  1. 每个节点非黑即红
  2. 根节点一定是黑色
  3. 叶子节点(NIL)是黑色
  4. 如果节点是红色,那么它的两个子节点都是黑色
  5. 从根节点到叶节点的所有路径,黑色节点数量相同
  • 由性质4、5可推知:红黑树中,最长路径 == 2倍最短路径

💸Node

  1. NIL节点,作为空节点(叶子节点)使用,默认单重黑。这样便于操作
  2. 正常新建节点,默认color为红,这样最大程度不影响性质5
  3. 红节点:没有子节点 or 两个黑色子节点
  4. 黑节点:如果只有一个子节点,那么必定是红色

🔫RBTree

封装红黑树的主要方法插入删除

  • 由于根节点到路径长度最坏相差一半,所以红黑树是一个基本平衡的二叉搜索树。
  • 没有AVL树那么绝对平衡,但是红黑树相比AVL旋转操作要少,而且删除操作也比AVL树效率更高。

👕insert

调整策略:

  1. 首先明确插入调整站在祖父节点看。(相当于做了一个延时操作
  2. 其次插入调整是为了解决两个红色节点相邻的问题

🐱Case1 🐱

黑红红: 采取红色上浮,变为红黑黑

在这里插入图片描述


🐶Case2🐶

黑红黑:LL失衡,大右旋(如下图2),变为红黑黑
在这里插入图片描述

在这里插入图片描述


🍗erase

调整策略:

  1. 红色节点

    • 度0:直接删
    • 度1:不存在
    • 度2:转为前驱
  2. 黑色节点

    • 度0:双黑顶替(延时)
    • 度1:红色点互换
    • 度2:转为前驱
  • 首先明确删除调整是站在父节点
  • 其次删除调整是为了调整双黑节点

🐯Case1🐯

兄弟黑+兄弟孩子黑:双黑上浮,黑红黑

在这里插入图片描述


🐻Case2🐻

兄弟黑+兄弟同侧孩子红:RR失衡,大左旋(如下图2),根变孩子颜色(右旋右孩子),孩子都变黑,双黑少一重

在这里插入图片描述

在这里插入图片描述


🐼Case3🐼

同case2: 不过先小旋(新根变黑,老根变红),在大旋

在这里插入图片描述

在这里插入图片描述


  • 如果兄弟红,那么就旋成黑,就可以啦(O(∩_∩)O)

不要忘了,每次插入或删除操作后,根强制为黑


💚💚💚为啥不是绿树?💗💗💗

2022年4月14日23点36分
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღCauchyོꦿ࿐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值