红黑树的删除(二):自顶向下

自顶向下的删除

  自顶向下的删除方法也是经典教材Robert Sedgewick的Algorithms中所讲述的,有兴趣的读者可以翻看Algorithms第四版。因此,本文在其基础上,讲一讲我对自顶向下删除的理解与书中代码的错误。先上代码。

import matplotlib.pyplot as plt


def is_red(node):
    if not node:
        return False
    return node.color


class Node(object):
    def __init__(self, x, left=None, right=None, parent=None, color=False):
        """
        :param color: True for red, False for black
        """
        self.key = x
        self.left = left
        self.right = right
        self.parent = parent
        self.color = color

    def __color_flip(self):
        if self.parent:
            self.color = not self.color
        self.left.color = not self.left.color
        self.right.color = not self.right.color

    def __rotate_left(self):
        right = self.right
        self.right = right.left
        if self.right:
            self.right.parent = self
        right.left = self
        right.color = self.color
        right.parent = self.parent
        self.__set_parent_children(right)
        self.parent = right
        self.color = True

    def __rotate_right(self):
        left = self.left
        self.left = left.right
        if self.left:
            self.left.parent = self
        left.right = self
        left.color = self.color
        left.parent = self.parent
        self.__set_parent_children(left)
        self.parent = left
        self.color = True

    def __is_left(self):
        if self.parent:
            return self.parent.left == self
        return False

    def __set_parent_children(self, child):
        if self.parent:
            if self.__is_left():
                self.parent.left = child
            else:
                self.parent.right = child

    def add(self, key):
        if key < self.key:
            if self.left:
                self.left.add(key)
            else:
                self.left = Node(key, parent=self, color=True)
        else:
            if self.right:
                self.right.add(key)
            else:
                self.right = Node(key, parent=self, color=True)
        
        node = self
        if is_red(node.right) and not is_red(node.left):
            node.__rotate_left()
            node = node.parent
        if is_red(node.left) and is_red(node.left.left):
            node.__rotate_right()
            node = node.parent
        if is_red(node.left) and is_red(node.right):
            node.__color_flip()

    def delete(self, key):
        if key < self.key:
            if not is_red(self.left) and not is_red(self.left.left):
                self.move_red_left()
            self.left.delete(key)
        else:
            node = self
            if is_red(node.left):
                node.__rotate_right()
                node = node.parent
            if node.key == key and not node.right:
                node.__set_parent_children(None)
                node.parent.balance()
                return
            if not is_red(node.right) and not is_red(node.right.left):
                node.move_red_right()
            if node.key == key:
                min_node = node.right
                if min_node:
                    while min_node.left:
                        min_node = min_node.left
                node.key = min_node.key
                node.right.delete(min_node.key)
            else:
                node.right.delete(key)
	
 	def move_red_left(self):
        self.__color_flip()
        if is_red(self.right.left):
            self.right.__rotate_right()
            self.__rotate_left()

    def move_red_right(self):
        self.__color_flip()
        if is_red(self.left.left):
            self.__rotate_right()

    def balance(self):
        node = self
        while node:
            if is_red(node.right):
                node.__rotate_left()
                node = node.parent
            if is_red(node.left) and is_red(node.left.left):
                node.__rotate_right()
                node = node.parent
            if is_red(node.left) and is_red(node.right):
                node.__color_flip()
            node = node.parent

    def draw(self, x=0, y=0, is_root=False, height=0, is_terminal=False):
        if not is_root and is_terminal:
            assert not self.color
        plt.axis('off')
        node = self
        if not is_root:
            while node.parent:
                node = node.parent
        circle_color = 'black' if not node.color else 'red'
        plt.scatter(x, y, color=circle_color, s=1000)
        text_color = 'white' if circle_color == 'black' else 'black'
        plt.text(x, y, node.key, fontsize='large', color=text_color, horizontalalignment='center',
                 verticalalignment='center')
        delta_x = 0.6 ** (height - 10)
        delta_y = 0.6 ** (height + 10)
        if node.left:
            assert node.left.parent == node
            if is_terminal:
                assert self.key > self.left.key
                assert not (self.left.color and self.color)
            line_color = 'black' if not node.left.color else 'red'
            plt.plot([x, x - delta_x], [y, y - delta_y], color=line_color)
            node.left.draw(x - delta_x, y - delta_y, is_root=True, height=height + 1)
        if node.right:
            assert node.right.parent == node
            if is_terminal:
                assert self.key < self.right.key
                assert not (self.right.color and self.color)
            line_color = 'black' if not node.right.color else 'red'
            plt.plot([x, x + delta_x], [y, y - delta_y], color=line_color)
            node.right.draw(x + delta_x, y - delta_y, is_root=True, height=height + 1)
        if not is_root:
            plt.show()

class RBT(object):
    def __init__(self, x):
        self.__root = Node(x)

    def add(self, x):
        result = self.__root.add(x)
        while self.__root.parent:
            self.__root = self.__root.parent
        # self.__root.draw(is_terminal=True)
        return result is not False

    def delete(self, x):
        if not self.__root.left and not self.__root.right:
            print('最后一个节点')
            return
        result = self.__root.delete(x)
        while self.__root.parent:
            self.__root = self.__root.parent
        self.__root.draw(is_terminal=True)
        return result

    def draw(self):
        self.__root.draw()

思路

  书中代码有两个错误,分别位于__color_flipmove_red_right。我在研究书中代码时便感觉奇怪,书中代码不符合整体的代码思路,在查阅源码后,发现这两处的确有误。
  首先说说__rotate_left__rotate_right。本质上,这两个操作是在不破坏树的平衡性(多有叶节点的深度相等)的前提下,改变红线方向的操作。
  整体思路书中说的比较多,其实简单的说就一句话:将要删除的节点放在3-节点,甚至4-节点中进行删除,然后再对树进行修复。这个思路应该还比较好理解,但书中源码的一些细节性的操作令我很困惑,例如代码第92行:为什么当前节点的左子节点为红时,就要进行左旋?
  解答这个问题要先思考,自顶向下的删除与自底向上的删除的本质区别在哪?区别在于,自顶向下的删除在向上修复的过程中并不考虑当前节点的兄弟节点是什么情况,只考虑父节点的具体情况,根据其父节点的具体情况进行不同操作;而自底向上的删除在向上修复的过程中则不仅考虑了当前节点的父节点的具体情况,还考虑了其兄弟节点的具体情况,根据具体情况作出更详细的分类讨论。
  基于此,自顶向下的删除,在向下的路径中必须保证另一侧的子节点(路径上的节点的兄弟节点)在未来的修复过程中不会发生错误。因此当路径另一侧离路径较近的子节点中有红键时,需要将红键旋转到路径上进行统一修复。若能想通这一点,代码就比较好理解了。

总结

  比较两种方法,显然自顶向下的方法较简单,但理解上有困难,并且很难想到;而自底向上的删除虽然分类讨论比较复杂,但思路便于理解,比较容易想到。在完全理解两种方法后,我感到受益匪浅,也算是完全的理解了红黑树的删除。在此,与大家交流与分享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值