首页 > 其他分享 >红黑树

红黑树

时间:2023-01-12 23:11:43浏览次数:28  
标签:right parent color insertNode 红黑树 节点 left

1. 红黑树的概念

  红黑树从平衡二叉搜索树延伸出来的一种较为复杂的数据结构,它会对树的各个节点进行着色标记(红色和黑色),相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,(插入最多需要旋转2次,删除最多需要旋转3次),整体来说性能要优于AVL树。在插入和删除操作的时候依据节点的颜色向平衡的方向调整,可以在O(logN)时间内完成查找、插入和删除。

  红黑树需要满足的条件:

   1. 根节点为黑色

   2. 子节点可以为红色或者黑色

   3. 每个叶节点(NIL)为黑色

   4. 红色节点的两个子节点必须是黑色

   5. 从叶子节点到根的路径,不可以同时出现两个连续的红色节点

   6. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

   

 

 

                     红黑树

 

2. 旋转调整

  因为红黑树也是一棵二叉搜索树,在插入或删除节点的时候,也需要进行旋转来调整节点的位置,红黑树旋转包好:左旋和右旋

  1. 左旋

   

 

 

 

  2. 右旋

  

 

 

 

3. 红黑树插入节点

  1. 红黑树插入节点默认为红色,如果插入的点是黑色,那么就无需调整颜色,整棵树都是黑色,也满足红黑树的要求,没有意义

  2. 变色的规则:

   1. 如果插入节点的父节点是红色,而且他的叔叔节点(父节点的兄弟节点)也是红色

    

    1. 把父节点(7)设为黑色

    2.把叔叔节点(13)设为黑色

    3.把祖父节点(12)设为红色

    4.把指针定义到祖父节点(12)分析是否符合红黑树性质要求

   2. 如果当前节点(12)为右节点,且它的父节点(5)是红色,叔叔节点(30)是黑色,不满足变颜色的需求,需要进行左旋

    1. 当前节点(12)向上移动作为根节点,他的父节点(5)向左下移动,作为它的左孩子

    2. 它的左孩子变成它的原来的父节点(5)的右孩子

    3. 它的右孩子不变

   3. 当前节点(5)为左节点,且父节点(12)是红色,叔叔节点(30)为黑色,进行右旋

    1. 把父节点(12)变为黑色

    2. 把祖父节点(19)变为红色

    3. 把祖父节点(19)进行右旋

    4. 父节点(12)的右孩子变成祖父节点(19)的左孩子

4. 红黑树的插入和删除实现

import random

# 节点的类型,颜色为黑色
class NullNode:
    def __init__(self):
        self.color = "Black"
        self.parent = None
        self.left = None
        self.right = None

# 红黑树节点类
class RbTree:
    def __init__(self, item, color):
        self.item = item
        self.color = color
        self.parent = NullNode()    # 父节点
        self.left = NullNode()      # 左孩子
        self.right = NullNode()     # 右孩子

    # 格式化输出: \033[30m~\033[37m 设置字体颜色,\033[0m 关闭所有属性
    def format(self):
        if self.color == "Red":
            return f"\033[31m{self.item}\033[0m"
        else:
            return f"\033[34m{self.item}\033[0m"

    # 左旋函数,x为颜色冲撞的上层节点,图中节点5, rootNode为根节点
    def left_rotate(self, rootNode, x):
        p = x.parent    # 指针节点的父节点
        r = x.right     # 指针节点的右孩子集合
        # 判断是否存在右孩子r,不存在则无法进行左旋
        if isinstance(r, NullNode):
            raise Exception("can not left_rotate while right child is Null ")

        # 右节点r设为父节点,即和x的父节点连接
        r.parent = p
        if x == p.left:     # 情况1,x是它父节点的左孩子,则r为p的左孩子
            p.left = r
        else:               # 情况2:x是它父节点的右孩子,则r为p的右孩子
            p.right = r

        # 右节点r的左节点变为x的右节点,即定义parent和right指向
        r.left.parent = x
        x.right = r.left

        # x设为它原来右孩子r的左节点,这里都要注意他们的双向关系
        r.left = x
        x.parent = r

        # 如果根节点发送变化,则t为新的根节点
        if not r.parent or isinstance(r.parent, NullNode):
            rootNode = r
        return rootNode

    # 右旋函数,x为当前颜色冲撞的上层节点的父节点,即图的节点19, rootNode为根节点
    def right_rotate(self, rootNode, x):
        p = x.parent
        l = x.left      # 左节点,即颜色冲突的上层节点,图节点12
        # 左孩不存在,无法进行旋转
        if isinstance(l, NullNode):
            raise Exception("can not right_rotate while left child is Null ")

        # 将x的左节点和x右旋,x的左节点为根节点,x为x的左节点的孩子
        l.parent = p
        if x == p.left:         # 情况1,x是它父节点的左孩子,则l为p的左孩子
            p.left = l
        else:
            p.right = l         # 情况2:x是它父节点的右孩子,则l为p的右孩子

        # 左节点l的右孩子为x的左孩子,将他们连接起来
        l.right.parent = x
        x.left = l.right

        # 将x为他原来左孩子l的右孩子,建立连接
        l.right = x
        x.parent = l

        if not l.parent or isinstance(l.parent, NullNode):
            rootNode = l
        return rootNode

    # 插入节点,rootNode为根节点,item为插入元素
    def insert(self, rootNode, item):
        # 插入节点,初始色为红色
        insertNode = RbTree(item, "Red")

        y = NullNode()      # y为节点的特征:黑色,且左右节点,父节点都为None
        x = rootNode        # x为根节点

        # 当x存在,并且x不是NullNode的实例对象时,一层一层对比往下插入,知道到达最底层
        while x and not isinstance(x, NullNode):
            y = x
            if insertNode.item < x.item:    # 小于根节点则往左下走
                x = x.left
            else:
                x = x.right     # 大于根节点则往右上走

        # 连接插入节点和指针节点,注意双重指向
        insertNode.parent = y
        # 判断节点插入的是指针节点的左边还是右边,空树的时候,直接插入
        if not y or isinstance(y, NullNode):
            rootNode = insertNode
        # 小于的时候,插入左边
        elif insertNode.item < y.item:
            y.left = insertNode
        # 小于的时候,插入右边
        else:
            y.right = insertNode
        # 返回根节点,insert_fixup的结果就是返回根节点
        return self.insert_fixup(rootNode, insertNode)

    # 调整颜色并进行旋转调整位置
    def insert_fixup(self, rootNode, insertNode):
        # 当插入节点的父节点为Red时,说明两个节点连续为Red,不符合
        while insertNode.parent.color == "Red":
            # 当插入节点的父节点是左子树时,并且它的兄弟节点为Red时,开始进行变色
            if insertNode.parent == insertNode.parent.parent.left:
                uncleNodeRight = insertNode.parent.parent.right
                if uncleNodeRight.color == "Red":
                    # 变色规则:父节点和叔叔节点变为Black,祖父节点变为Red
                    insertNode.parent.color = "Black"
                    uncleNodeRight.color = "Black"
                    insertNode.parent.parent.color = "Red"
                    insertNode = insertNode.parent.parent       # 将指针更新值祖父节点处
                # 当叔叔节点时Black时
                else:
                    # 当节点插入的是右边时,右子树左旋
                    if insertNode == insertNode.parent.right:
                        insertNode = insertNode.parent          # 更新一下指着,即插入元素的父节点
                        rootNode = self.left_rotate(rootNode, insertNode)   # 父节点左旋

                    insertNode.parent.color = "Black"           # 父节点变Black
                    insertNode.parent.parent.color = "Red"      # 祖父节点变Red
                    rootNode = self.right_rotate(rootNode, insertNode.parent.parent)  # 以祖父节点为指针进行右旋

            # 当插入节点的父节点是右子树时,并且它的兄弟节点为Red时,开始进行变色,和左边一样
            else:
                uncleNodeLeft = insertNode.parent.parent.left
                if uncleNodeLeft.color == "Red":        # 叔叔节点
                    insertNode.parent.color = "Black"   # 父节点
                    uncleNodeLeft.color = "Black"       # 叔叔节点
                    insertNode.parent.parent.color = "Red"  # 祖父节点
                    insertNode = insertNode.parent.parent
                # 当节点插入的是左边时,左子树右旋
                else:
                    if insertNode == insertNode.parent.left:
                        insertNode = insertNode.parent
                        rootNode = self.right_rotate(rootNode, insertNode)  # 右旋

                    # 父节点变黑,原来的祖父节点变红,并进行旋转
                    insertNode.parent.color = "Black"
                    insertNode.parent.parent.color = "Red"
                    rootNode = self.left_rotate(rootNode, insertNode.parent.parent)

        # 将根节点变为Black,并返回
        rootNode.color = "Black"
        return rootNode

    # 删除节点,rootNode为根节点,item为删除的元素
    def delete(self, rootNode, item):
        deleteNode = NullNode()     # 指针,指向删除的节点
        y = NullNode()
        x = rootNode        # 先把根节点存起来
        # 当根节点存在,就开始进行寻找元素的位置
        while x and not isinstance(x, NullNode):
            # 三种情况:大于指针节点往右找,小于往左找,并更新x的位置,等于说明找到了,将元素位置赋值给指针
            if item < x.item:
                x = x.left
            elif item > x.item:
                x = x.right
            else:
                deleteNode = x
                break

        if isinstance(deleteNode, NullNode):
            return rootNode

        if isinstance(deleteNode.left, NullNode) or isinstance(deleteNode.right, NullNode):
            y = deleteNode
        else:
            y = self.tree_successor(deleteNode)

        if not isinstance(y.left, NullNode):
            x = y.left
        else:
            x = y.right

        x.parent = y.parent
        if isinstance(y.parent, NullNode):
            rootNode = x
        elif y == y.parent.left:
            y.parent.left = x
        else:
            y.parent.right = x

        if y != deleteNode:
            deleteNode.item = y.item

        if y.color == "Black":
            rootNode = self.delete_fixup(t, x)

        print(rootNode.item)
        return rootNode


    def delete_fixup(self, t, x):
        while x != t and x.color == "Black":
            if x == x.parent.left:
                w = x.parent.right
                if w.color == "Red":
                    w.color = "Black"
                    x.parent.color = "Red"
                    t = self.left_rotate(t, x.parent)
                    w = x.parent.right
                if w.left.color == "Black" and w.right.color == "Black":
                    w.color = "Red"
                    x = x.parent
                elif w.right.color == "Black":
                    w.left.color = "Black"
                    w.color = "Red"
                    t = self.right_rotate(t, w)
                    w = x.parent.right
                else:
                    w.color = x.parent.color
                    x.parent.color = "Black"
                    w.right.color = "Black"
                    t = self.left_rotate(t, x.parent)
                    x = t
            else:
                w = x.parent.left
                if w.color == "Red":
                    w.color = "Black"
                    x.parent.color = "Red"
                    t = self.right_rotate(t, x.parent)
                    w = x.parent.left
                if w.right.color == "Black" and w.left.color == "Black":
                    w.color = "Red"
                    x = x.parent
                elif w.left.color == "Black":
                    w.right.color = "Black"
                    w.color = "Red"
                    t = self.left_rotate(t, w)
                    w = x.parent.left
                else:
                    w.color = x.parent.color
                    x.parent.color = "Black"
                    w.left.color = "Black"
                    t = self.right_rotate(t, x.parent)
                    x = t

        x.color = "Black"
        return t


    def tree_successor(self, x):
        y = x.right
        insertNode = NullNode()
        while y and not isinstance(y, NullNode):
            insertNode = y
            y = y.left

        return insertNode


    def set_parent(self, p, l, r):
        p.left = l
        p.right = r
        l.parent = p
        r.parent = p


    def format_tree(self, t):
        if not t or isinstance(t, NullNode):
            return 0, []

        left_indent, left_tree = self.format_tree(t.left)
        right_indent, right_tree = self.format_tree(t.right)

        tree = []
        tree.append("." * left_indent + t.format() + "." * right_indent)
        n = 0
        while n < len(left_tree) or n < len(right_tree):
            line = ""
            if n < len(left_tree):
                line += left_tree[n]
            else:
                line += " " * left_indent

            line += " " * len(str(t.item))
            if n < len(right_tree):
                line += right_tree[n]
            else:
                line += " " * right_indent
            tree.append(line)

            n += 1

        indent = left_indent + len(str(t.item)) + right_indent
        return indent, tree

    def print_tree(self, t):
        _, tree = self.format_tree(t)
        for line in tree:
            print(line)


t = NullNode()

li = [i for i in range(10)]
random.shuffle(li)
for i in li:
    tree = RbTree(i, "Red")
    t = tree.insert(t, i)
tree.print_tree(t)
tree.delete(t,4)
tree.print_tree(t)

 

标签:right,parent,color,insertNode,红黑树,节点,left
From: https://www.cnblogs.com/chf333/p/17048224.html

相关文章

  • 红黑树
    13213997472小的左边,大的右边左左先右旋左右先一出问题左旋再右旋右右左旋先右在左队列:先进先出,后进后出zan:后进先出,先进后出。数组:内存连续区域,查询快,增删慢。链表......
  • Mysql为什么用B+树做索引而不用B-树或红黑树?
    一、概述B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域。所以从Mysql(Inoodb)的角度来看,B+树是用来充当索引的,一般来说索引非常大,尤其是关系性数......
  • 面试让我手写红黑树?!
    作者:小傅哥博客:​​​https://bugstack.cn​​沉淀、分享、成长,让自己和他人都能有所收获!......
  • Linux面试必备的红黑树问题,这可能是zui全的一篇了!
    原文网址:https://zhuanlan.zhihu.com/p/471318214首先上一个红黑树知识点结构图1.stl中的set底层用的什么数据结构?2.红黑树的数据结构怎么定义的?3.红黑树有哪些性......
  • 红黑树起源以及插入解析
    红黑树的起源二分查找具有Ologn的时间复杂度,使用二分查找的基础是数据有序。很明显数组可以完成这一条件,但是数组也有缺点,扩容,增加,删除非常不方便。而链表则没有这些缺点,......
  • 红黑树实现代码
    红黑树演变过程可参考该网站动画演示Red/BlackTreeVisualization(usfca.edu)红黑树平衡树通过左旋右旋 四种旋转情况LL->R(祖父节点)LR->L(父节点)R(祖父节点)RR->L(......
  • 红黑树遍历方法
    二叉树之前序遍历  二叉树之中序遍历  二叉树之层序遍历  二叉树之后序遍历  拓展:红黑树的内容   ......
  • 数据结构高阶--红黑树(图解+实现)
    红黑树概念和性质红黑树的概念:红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。它是通过控制节点颜色的方式来控制这棵树的相对平......
  • 二叉树入门到进阶(下一篇讲红黑树)——c语言刷题合集
    目录二叉树概念二叉树的遍历方式DFS(前序中序后序遍历)144.二叉树的前序遍历递归解法迭代解法94.二叉树的中序遍历145.二叉树的后序遍历层序遍历--队列的作用102.二叉......
  • 题解——红黑树
    进制运算-红黑树题目描述红黑树是一类特殊的二叉搜索树,其中每个结点被染成红色或黑色。若将二叉搜索树结点中的空指针看作是指向一个空结点,则称这类空结点为二叉搜索树的......