阅读 60

红黑树

⒈ 红黑树的属性

  • 每个节点的颜色非红即黑
  • 根节点永远是黑色
  • 所有叶子节点的值都是 NULL,颜色都是黑色
  • 红色节点的父节点和子节点都是黑色
  • 从一个节点到其所属的叶子节点的任意路径上所包含的黑色节点的数量相等
  • 一个节点到其最远叶子节点的路径上所包含的节点数量不会超过到其最近叶子节点的路径上所包含节点数量的两倍

⒉ 红黑树的高度及平衡

  红黑树也是一种自平衡二叉树,但红黑树的平衡并不是像 AVL 树那样严格的平衡。红黑树的平衡指的是从根节点到任何叶子节点的路径上所包含的黑色节点的数量相等。

  综上所述,设红黑树的节点总数为 n,红黑树的高度为h,从根节点到叶子节点的最短路径包含的节点数量为 k,则有 n>=2k1n >= 2^k - 1,即 k<=log2(n+1)k <= \log_2(n + 1) 。由于红黑树从根节点到所有叶子节点路径上的黑色节点数量相等,所以红黑树黑色节点的总数最多为 2k12^k - 1,即黑色节点的高度最多为 k 。而红黑树中红色节点的父节点和子节点都是黑色,所以 k<=h2k <= \frac{h}{2},所以红黑树的高度 h<=2log2(n+1)h <= 2\log_2(n + 1)

⒊ 红黑树节点的插入

  • 首先按照 BST 的插入规则插入新的节点 x,所有新插入的节点默认都为红色
  • 如果 x 为根节点,则将 x 的颜色标记为黑色
  • 如果 x 不是根节点并且 x 的父节点不是黑色,则需要根据不同情形进行适当的重新着色或旋转节点来重新实现红黑树的平衡

   设 x 的父节点为 p,p 的父节点为 g,p 的兄弟节点为 u

   ① 如果 u 为红色

   ② 如果 u 为黑色

     ⅰ 如果 x 为 p 的左节点,p 为 g 的左节点

     ⅱ 如果 x 为 p 的右节点,p 为 g 的左节点

     ⅲ 如果 x 为 p 的右节点,p 为 g 的右节点(ⅰ 的镜像)
     ⅳ 如果 x 为 p 的左节点,p 为 g 的右节点(ⅱ 的镜像)

   代码实现

class Node:
    def __init__(self, key):
        self.item = key
        self.parent = None
        self.left = None
        self.right = None
        self.color = 1

    def __repr__(self):
        return str(self.__dict__)

    def __str__(self):
        return str(self.__dict__)


class RBTree:
    def __init__(self):
        self.root = None

    def insert(self, k):
        """
        插入节点
        :param k:
        :return:
        """
        node = Node(k)

        if self.root is None:
            node.color = 0
            self.root = node
            return

        current = self.root
        while current is not None:
            parent = current
            if node.item < current.item:
                current = current.left
            else:
                current = current.right

        node.parent = parent
        if node.item < parent.item:
            parent.left = node
        else:
            parent.right = node

        self.fix_tree(node)

    def fix_tree(self, node):
        """
        调整节点平衡
        :param node:
        :return:
        """
        while node != self.root and node.parent.color == 1:
            if node.parent == node.parent.parent.left:
                u = node.parent.parent.right
                if u.color == 1:
                    u.color = 0
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    node = node.parent.parent
                else:
                    if node == node.parent.right:
                        node = node.parent
                        self.left_rotate(node)
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    self.right_rotate(node.parent.parent)
            else:
                u = node.parent.parent.left
                if u.color == 1:
                    u.color = 0
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    node = node.parent.parent
                else:
                    if node == node.parent.left:
                        node = node.parent
                        self.right_rotate(node)
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    self.left_rotate(node.parent.parent)
        self.root.color = 0

    def left_rotate(self, node):
        new = node.right
        node.right = new.left

        if new.left is not None:
            new.left.parent = node

        new.parent = node.parent
        if node.parent is None:
            self.root = new
        elif node == node.parent.left:
            node.parent.left = new
        else:
            node.parent.right = new
        new.left = node
        node.parent = new

    def right_rotate(self, node):
        new = node.left
        node.left = new.right

        if new.right is not None:
            new.right.parent = node

        new.parent = node.parent
        if node.parent is None:
            self.root = new
        elif node == node.parent.left:
            node.parent.left = new
        else:
            node.parent.right = new

        node.parent = new
        new.right = node


rbt = RBTree()
rbt.insert(55)
rbt.insert(40)
rbt.insert(65)
rbt.insert(60)
rbt.insert(75)
rbt.insert(57)

print(rbt.root)

复制代码

⒋ 红黑树节点的删除

   节点删除按照 BST 的节点删除规则操作,最终要么删除一个叶子节点,要么删除一个只包含一个子节点的节点。设要删除的节点为 v,取代节点 v 的位置的节点为 u(红黑树中叶子节点为 NULL,所以红黑树中的节点删除不会删除真正的叶子节点,但会删除叶子节点的父节点,此时 u 为 NULL,红黑树中 NULL 为黑色)。

  ① u 和 v 中有一个红色节点,这种情况处理起来比较简单,只需要将 u 标记为黑色即可

  ② u 和 v 都是黑色,此时删除 v 会破坏红黑树的平衡,我们要做的就是恢复红黑树的平衡

   设 v 的兄弟节点为 s,v 的父节点为 p

     ⅰ 如果 s 为黑色,s 的子节点至少由一个为红色

     设 s 的红色子节点为 r,根据 r 和 s 的相对位置,可以分为四种情况

       a. s 为 p 的左子节点,r 为 s 的左子节点

       b. s 为 p 的左子节点,r 为 s 的右子节点

       c. s 为 p 的右子节点,r 为 s 的右子节点(a 的镜像)
       d. s 为 p 的右子节点,r 为 s 的左子节点(b 的镜像)
     ⅱ 如果 s 和 s 的子节点都为黑色

     ⅲ 如果 s 为红色

   代码实现

    def search(self, node, k):
        if node is None:
            return None
        if k == node.item:
            return node
        if k < node.item:
            return self.search(node.left, k)
        else:
            return self.search(node.right, k)

    def delete(self, node):
        """
        节点删除
        :param node:
        :return:
        """
        if isinstance(node, int):
            node = self.search(self.root, node)
            if node is None:
                return

        replace = self.find_replace(node)

        double_black = ((replace is None or replace.color == 0) and (node.color == 0))
        parent = node.parent
        if parent is None:
            sibling = None
        elif node == parent.left:
            sibling = parent.right
        else:
            sibling = parent.left

        if replace is None:
            if node == self.root:
                self.root = None
            else:
                if double_black:
                    # node 和 replace 都是黑色
                    self.fix_double_black(node)
                else:
                    # replace 是叶子节点,所以是黑色,故 node 是红色
                    if sibling is not None:
                        sibling.color = 1

                if node == parent.left:
                    parent.left = None
                else:
                    parent.right = None
            return

        if node.left is None or node.right is None:
            if node == self.root:
                node.item = replace.item
                node.left = None
                node.right = None
            else:
                if node == parent.left:
                    parent.left = replace
                else:
                    parent.right = replace
                replace.parent = parent

                if double_black:
                    self.fix_double_black(replace)
                else:
                    replace.color = 0
            return

        # 有两个子节点
        temp = node.item
        node.item = replace.item
        replace.item = temp
        self.delete(replace)

    @staticmethod
    def find_replace(node):
        # 左右子节点都为空
        if node.left is None and node.right is None:
            return None
        # 左右子节点都不为空
        if node.left is not None and node.right is not None:
            successor = node.right
            while successor.left is not None:
                successor = successor.left
            return successor
        # 左右子节点有一个不为空
        if node.left is not None:
            return node.left
        else:
            return node.right

    def fix_double_black(self, node):
        if node == self.root:
            return

        parent = node.parent
        if parent is None:
            sibling = None
        elif node == parent.left:
            sibling = parent.right
        else:
            sibling = parent.left
        
        if sibling is None:
            self.fix_double_black(parent)
        else:
            if sibling.color == 1:
                sibling.color = 0
                parent.color = 1

                if sibling == parent.left:
                    self.right_rotate(parent)
                else:
                    self.left_rotate(parent)

                self.fix_double_black(node)
            else:
                if sibling.left is not None and sibling.left.color == 1:
                    if sibling == parent.left:
                        sibling.left.color = sibling.color
                        sibling.color = parent.color
                        self.right_rotate(parent)
                    else:
                        sibling.left.color = parent.color
                        self.right_rotate(sibling)
                        self.left_rotate(parent)
                    parent.color = 0
                elif sibling.right is not None and sibling.right.color == 1:
                    if sibling == parent.left:
                        sibling.right.color = parent.color
                        self.left_rotate(sibling)
                        self.right_rotate(parent)
                    else:
                        sibling.right.color = sibling.color
                        sibling.color = parent.color
                        self.left_rotate(parent)
                    parent.color = 0
                else:
                    sibling.color = 1
                    if parent.color == 0:
                        self.fix_double_black(parent)
                    else:
                        parent.color = 0
复制代码

⒌ 红黑树与 AVL 的比较

  • AVL 树的平衡相较于红黑树更加严格,所以节点数量相同的 AVL 树的高度通常会小于红黑树的高度,这样 AVL 树的搜索会更快
  • 同样由于红黑树的平衡并没有 AVL 树的严格,所以在插入或删除节点时红黑树的旋转操作通常会比较少,这样红黑树的插入和删除操作通常会更快
文章分类
后端
文章标签