红黑树的删除(一):自底向上

红黑树的删除

  红黑树的添加操作较为简单,网上有许多详细代码与解释,本文不再赘述。关于红黑树的删除,我个人分为两种方法:自顶向下与自底向上。自底向上的删除是在寻找继承节点的路径上不作操作,找到继承节点后根据其兄弟节点与父节点的具体情况分类讨论,层层向上对树进行修复;自顶向下的删除是从树的根节点开始,将从根节点到继承节点路径上的所有节点进行统一的操作,删除目标节点后,再向上对树进行统一修复。两种方法各有优劣。本文将对自底向上的删除方法进行详解。

定义

概念

  • 3-节点:由一个红节点连接一个黑节点组成。例:(2-7)(7.2-7.3)。为便于描述,将位于3-节点中的节点直接称为3-节点。例:节点23-节点。对于不是叶节点的3-节点,有左、中、右三个子节点。例:节点1节点3节点(7.2-7.3) 分别为节点(2-7)的左子节点、中子节点、右子节点。
  • 2-节点:由一个左子节点不为红节点的黑节点组成。例:节点1节点9
  • 叶节点:没有子节点的节点或有且仅有红色左子节点的节点。例:节点1节点3节点7.2节点7.3
  • 继承节点:待删除节点右侧的最小节点。例:节点9的继承节点为节点10节点7.5的继承节点为节点8
  • 相邻节点:左节点的父节点的右节点,或右节点的父节点的左节点。例:节点1节点3节点3节点7.3。这里的父节点可以是3-节点

操作

  • 交换:__swap。交换两个节点的位置,不改变其它节点的位置。
  • 嫁接:__graft。将节点嫁接到目标节点的左侧或右侧,目标节点原先对应的子节点信息将丢失。
  • 合并子节点:merge_children。将目标节点的左子节点与右子节点合并为3-节点嫁接到目标节点左侧,将目标节点右子节点变为None。该操作为Case C中的独有操作,因此不会出现目标节点的左子节点与右子节点均为3-节点的情况。

寻找继承节点

  为便于描述,将待删除节点记为root。若root为叶节点,则直接进入下一步。否则,寻找继承节点,并与之交换。

自底向上的删除

  此时root已为叶节点。
  若root3-节点,则删除操作较简单。

  • root3-节点中的较小节点,直接删除。例:删除节点7.2,删除节点7
  • root3-节点中的较大节点,与3-节点中的较小节点交换后删除。例:删除节点7.3

  若root位于2-节点,则分以下三种情况:

  • root的相邻节点为3-节点,记为Case A。例:删除节点3
  • root的相邻节点为2-节点,父节点为3-节点,记为Case B。例:删除节点1
  • root的相邻节点为2-节点,父节点也为2-节点,记为Case C。例:删除节点8

  每种情况下还有许多小情况,如:root是其父节点的左子节点还是右子节点;root相邻的3-节点在其左侧还是右侧。随着这些情况的不同,会对删除操作产生一定影响,但原理都一样,因此下文中,对于这些小情况不在细分讲解。可以直接阅读代码进行理解。

Case A

  对于此类情况,核心思想为:

将合适的节点node填补到原root的位置。将相邻的3-节点变为2-节点,合理移动多出的节点填补node的空缺。

  以删除节点3为例。节点3的相邻节点节点(7.2-7.3)3-节点

  • 节点3节点7交换(左上)。
  • 节点3节点7.2交换(右上)。
  • 节点7.3嫁接到节点7.2右侧并改变其颜色(左下)。

Case B

  对于此类情况,核心思想为:

将父节点中合适的节点填补到原root的位置,将父节点变为2-节点。再根据具体情况进行调整。

  以删除节点1为例。节点1的相邻节点32-节点,父节点(2-7)3-节点

  • 节点3嫁接到节点7左侧(左上)。
  • 节点2嫁接到节点3左侧(右上)。
  • 删除节点1(左下)。

Case C

  该情况是最复杂的情况。对于此类情况,核心思想为:

上移root,直至root满足Case A或Case B或root是整棵树的根节点。
Case C to Case A

  以删除节点8为例。其相邻节点节点10,父节点节点9均为2-节点

  • 上移节点8。此时,节点8的相邻节点节点(2-7)3-节点,满足Case A的条件(左上)。
  • 合并节点8的子节点(右上)。
  • 进行(部分)Case A的操作(左下)。
  • 节点10嫁接到节点7.5的右侧,将节点7.3嫁接到节点7.5的左侧(右下)。
Case C to Case B

  先添加节点7.1,添加后的树如下图所示。

  以删除节点1为例。其相邻节点节点3,父节点节点2均为2-节点

  • 上移节点1。此时,节点1的相邻节点节点7.22-节点,父节点节点(7-7.5)3-节点,满足Case B的条件(左上)。
  • 合并节点1的子节点(右上)。
  • 进行(部分)Case B的操作(左中)。
  • 节点7嫁接到节点7.2的左侧,将节点7.1嫁接到节点7的右侧(右中)。
  • 节点3嫁接到节点7左侧(左下)。
Case C to Case C

  为更好说明此情况,用如下树进行说明:

  以删除节点1为例。其相邻节点节点3,父节点节点2均为2-节点

  • 上移节点1。此时,节点1的相邻节点节点62-节点,父节点节点42-节点,不满足Case ACase B的条件(左上)。
  • 合并节点1的子节点并上移节点1。此时,节点1的相邻节点节点122-节点,父节点节点82-节点,不满足Case ACase B的条件(右上)。
  • 合并节点1的子节点并上移节点1。此时,节点1是整棵树的根节点(左下)。
  • 节点8节点12合并为3-节点,并将其作为新的根节点(右下)。

  若过程中发现root满足Case ACase B的条件,则直接转化为Case C to Case ACase C to Case B即可。

代码

import matplotlib.pyplot as plt


class Node(object):
    def __init__(self, x, left=None, right=None, parent=None, color=False):
        """

        :param color: True for red, False for black
        """
        self.label = x
        self.left = left
        self.right = right
        self.parent = parent
        self.color = color

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

    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
        if self.parent:
            if self.__is_left():
                right.parent.left = right
            else:
                right.parent.right = 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
        if self.parent:
            if self.__is_left():
                left.parent.left = left
            else:
                left.parent.right = left
        self.parent = left
        self.color = True

    def __is_leaf(self):
        return not self.left and not self.right

    def __swap(self, node):
        if self.parent:
            if self.__is_left():
                self.parent.left = node
            else:
                self.parent.right = node
        if node.parent:
            if node.parent.left == node:
                node.parent.left = self
            else:
                node.parent.right = self
        node.parent, self.parent = self.parent, node.parent
        node.color, self.color = self.color, node.color
        node.left, self.left = self.left, node.left
        if node.left:
            node.left.parent = node
        if self.left:
            self.left.parent = self
        node.right, self.right = self.right, node.right
        if node.right:
            node.right.parent = node
        if self.right:
            self.right.parent = self

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

    def __is_right(self):
        if self.parent:
            return self.parent.right == self
        else:
            return False

    def __graft(self, target_node, direction):
        if not target_node:
            self.parent.parent = self
            self.parent = None
            return
        if direction:
            target_node.left = self
        else:
            target_node.right = self
        self.parent = target_node

    def __steal_from_parent(self):
        # 父节点是3-节点且相邻兄弟节点为2-节点
        if self.__is_left() and self.parent.color:
            # 删除左侧的节点
            self.parent = self.parent
            self.parent.right.__graft(self.parent.parent, True)
            if self.left and not self.right:
                # 从__merge运行到这里
                self.parent.right.left.__graft(self.parent, False)
                self.parent.__graft(self.parent.parent.left, True)
                self.parent.left.left.__graft(self.parent, True)
                self.parent.color = True
                return True
            self.parent.right = None
            self.parent.__graft(self.parent.parent.left, True)
            self.parent.left = None
            return True
        elif self.__is_right() and self.parent.color:
            # 删除中间节点
            self.parent.left.color = True
            self.parent.color = False
            if self.left and not self.right:
                self.left.__graft(self.parent, False)
                return True
            self.parent.right = None
            return True
        elif self.__is_right() and self.parent.left.color:
            self.parent.left.__graft(self.parent.parent, self.parent.__is_left())
            self.parent.left.color = False
            if self.left and not self.right:
                # 从__merge运行到这里
                self.parent.left.right.__graft(self.parent, True)
                self.parent.left.color = True
                self.parent.__graft(self.parent.parent, False)
                self.parent.right.left.__graft(self.parent, False)
                return True
            self.parent.__graft(self.parent.left.right, True)
            self.parent.left, self.parent.right = None, None
            self.parent.color = True
            self.parent.__swap(self.parent.parent)
            return True
        return False

    def __steal_from_siblings(self):
            def __steal_from_siblings(self):
        def switch_to_siblings(node: Node, direction):
            node.__swap(node.parent) if direction != 'mr' else node.__swap(node.parent.parent)
            if direction == 'lm' or direction == 'mr':
                node.__swap(node.right.left)
                node.__swap(node.parent)
            elif direction == 'ml':
                node.__swap(node.left)
            elif direction == 'rm':
                node.__swap(node.left.right)
            if node.right:
                # 若存在右子节点,说明是从__swim_up运行到这里
                if node.__is_right():
                    switch_node = node.left.left
                    node.left.__swap(node.parent)
                    node.__swap(node.left)
                    node.right.__graft(node.parent, True)
                    return switch_node, direction
                else:
                    switch_node = node.right
                    node.left.__graft(node.parent, True)
                    node.left.color = False
                    return switch_node, direction
            else:
                node.left.__graft(node.parent, node.__is_left())
                node.left.color = False
            return True
            
        # 当父节点为2-节点时或父节点为3-节点但不涉及到右侧节点时
        if self.__is_left() and self.parent.right.left and self.parent.right.left.color:
            # 删除左节点且右(中间)节点为3-节点
            return switch_to_siblings(self, 'lm')
        elif self.__is_right() and self.parent.left.left and self.parent.left.left.color:
            # 删除右(中间)节点且左节点为3-节点
            return switch_to_siblings(self, 'ml')
        # 当父节点为3-节点且涉及到右侧节点时
        elif self.__is_right() and self.parent.left.color and self.parent.left.right.left \
                and self.parent.left.right.left.color:
            # 当删除右侧节点,且中间节点为3-节点时
            return switch_to_siblings(self, 'rm')
        elif self.__is_right() and self.parent.color and self.parent.parent.right.left \
                and self.parent.parent.right.left.color:
            # 当删除中间节点,且右节点为3-节点时:
            return switch_to_siblings(self, 'mr')
        return False

    def __swim_up(self):
        def merge_children(node: Node):
            if not node.left and not node.right:
                # 底层合并
                node.right.__graft(node.left, True)
                node.left.left.color = True
                node.right = None
                node.left.__swap(node.left.left)
                return
            else:
                if node.left.left and not node.left.right:
                    # 从左节点上游合并
                    node.right.left.__graft(node.left, False)
                elif node.right.left and not node.right.right:
                    # 从右节点上游合并
                    node.right.left.__graft(node.right, False)
                node.left.__graft(node.right, True)
                node.left.color = True
                node.right.__graft(node, True)
                node.right = None
                return

        self.__swap(self.parent)
        merge_children(self)
        temp = self.left
        if info := self.__steal_from_siblings():
            switch_node, direction = info[0], info[1]
            if direction == 'ml' or direction == 'rm':
                temp.parent.left.__graft(temp.parent, False)
                switch_node.__graft(temp.parent, True)
            else:
                switch_node.__graft(temp.parent, False)
            return True
        elif self.__steal_from_parent():
            return True
        else:
            if not self.parent:
                self.left.__graft(self.parent, False)
                return True
            self.__swim_up()
            return True

    def add(self, x):
        def fix(node):
            if node.color and node.left and node.left.color:
                node.parent.__rotate_right()
                node.__color_flip()
            elif node.left and node.left.color and node.right and node.right.color:
                node.__color_flip()
            elif ((node.left and node.left.color is False) or not node.left) and node.right and node.right.color:
                node.__rotate_left()
                node = node.parent
                if node.color:
                    node.parent.__rotate_right()
                    node.__color_flip()
            else:
                return
            if node.parent:
                node = node.parent
            fix(node)

        if self.__is_leaf():
            if self.color:
                if x == self.label or x == self.parent.label:
                    return False
                if x < self.label:
                    self.left = Node(x, parent=self, color=True)
                elif self.label < x < self.parent.label:
                    self.right = Node(x, parent=self, color=True)
                elif x > self.parent.label:
                    self.parent.right = Node(x, parent=self, color=True)
                fix(self)
            else:
                if x == self.label:
                    return False
                elif x < self.label:
                    self.left = Node(x, parent=self, color=True)
                elif x > self.label:
                    self.right = Node(x, parent=self, color=True)
                    self.__rotate_left()
        else:
            if x == self.label:
                return False
            elif x < self.label:
                if self.left is None:
                    self.left = Node(x, parent=self, color=True)
                    fix(self)
                else:
                    return self.left.add(x)
            elif x > self.label:
                if self.right is None:
                    self.right = Node(x, parent=self, color=True)
                    fix(self)
                else:
                    return self.right.add(x)

    def search(self, x):
        if x == self.label:
            return self
        elif x > self.label:
            if self.right is None:
                return False
            return self.right.search(x)
        elif x < self.label:
            if self.left is None:
                return False
            return self.left.search(x)

    def delete(self, x):
        if (root := self.search(x)) is False:
            return False
        if not root.__is_leaf() and not (root.left and root.left.__is_leaf() and root.left.color):
            # 若root不是叶节点,寻找继承节点并与其交换
            # 若root不是叶节点,则一定同时存在左右两个子节点
            successor = root.right
            while successor.left:
                successor = successor.left
            successor.__swap(root)

        # root已是叶节点
        if root.__is_leaf() and root.color:
            # 若root是3-节点较小键,直接删除
            root.parent.left = None
            return True
        elif root.left and root.left.color:
            # 若root是3-节点较大键,将较小键接到父节点上,并改变颜色。
            root.left.__graft(root.parent, self.__is_left())
            root.left.color = False
            return True
        elif root.__steal_from_siblings() is not False:
            # 若root的兄弟节点存在3-节点
            return True
        elif root.__steal_from_parent():
            # 若root的兄弟节点均为2-节点,父节点为3-节点
            return True
        elif root.__swim_up():
            return True

    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.label, 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
            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
            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 search(self, x):
        return self.__root.search(x)

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


if __name__ == '__main__':
    items = [7, 7.5, 2, 1, 3, 7.2, 7.1, 7.3, 9, 8, 10]
    rbt = RBT(items[0])
    for item in items[1:]:
        rbt.add(item)

    rbt.delete(7.1)
    # Case A: rbt.delete(3)
    # Case B: rbt.delete(1)
    # Case C-A: rbt.delete(8)
    # Case C-B: rbt.delete(7.1)

    # items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
    # rbt = RBT(items[0])
    # for item in items[1:]:
    #     rbt.add(item)
    # rbt.delete(16)
    # Case C-C: rbt.delete(1)

总结

  本文通过举例的方式将所有的大情况详细的讲述了一遍,实际上,如前文所说,各种大情况下,还有很多小情况,在编程过程中需要分别进行分类讨论,但原理与本文所举算例相似。在下一篇文章中,会讲解自顶向下的删除方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值