红黑树(Red Black Tree)基本性质 + 建树

news2025/2/1 12:08:36

定义


红黑树:一种特殊的二叉搜索树
二叉搜索树:一种树的类型,每个节点最多有两个子节点,其中其左节点一定小于当前节点,右节点一定大于当前节点

二叉树的缺点:如果给定的初始序列顺序不好,可能会建出类似于链表的结构,对搜索速度全无助益

请添加图片描述
红黑树的目的:构建一棵趋于平衡的二叉搜索树,杜绝bad case的出现情况

性质


红黑树树节点的组成

  • 左孩子 left (树节点)
  • 右孩子 right (树节点)
  • 父节点 parent (树节点)
  • 颜色 color (红/黑)
  • 值 value (任意)

红黑树的特点

  • 没有一条路径会比其他路径长出2倍

合法红黑树的性质

  • 每个节点都是红色或者黑色的
  • 根节点是黑色
  • 叶节点*是黑色
  • 如果一个节点是红色的,他的两个孩子都应该是黑色
  • 对每个结点,从该节点到其所有后代的叶子节点的简单路径上,包含相同数目的黑色节点

其中,最后一个性质确保了红黑树的相对“平衡”
*为了便于处理边界条件,红黑树中没有节点的孩子会直接指向None,通过定义一个通用哨兵NIL来代指None

NIL's param settings
	parent = None
	color = BLACK
	left = None
	right = None
	value = None

请输入图片描述
右图为红黑树的包含哨兵(nil)状态,为了方便,后图都将采用左图的方式绘制

基本操作(旋转)


旋转操作是红黑树的基本操作,旋转的示意图如下
请添加图片描述

旋转需要满足如下要求:

  • 左旋:需要左旋的树节点,其右孩子不为NIL
  • 右旋:需要右旋的树节点,其左孩子不为NIL

至于其他节点,由于存在哨兵NIL,变得易于处理,以左旋为例:

  • 事先定义:旋转的节点为N,它的右孩子为R,R的左孩子为RL(是NIL也无所谓),其他节点不需要关注。
  • S1:断开N和R
  • S2:N的右孩子为RL
  • S3:R的左孩子为N

下面给出左旋和右旋的python代码

    def _left_rotate(self, node):
        if node.right == self.nil:
            print("can't left rotate")
            return

        right = node.right
        parent = node.parent
        isLeft = parent.left == node

        if self.root == node:
            self.root = right

        if isLeft:
            parent.left = right
        else:
            parent.right = right
        right.parent = parent

        node.right = right.left
        right.left.parent = node

        right.left = node
        node.parent = right

    def _right_rotate(self, node):
        if node.left == self.nil:
            print("can't right rotate")
            return

        left = node.left
        parent = node.parent
        isLeft = parent.left == node

        if self.root == node:
            self.root = left

        if isLeft:
            parent.left = left
        else:
            parent.right = left
        left.parent = parent

        node.left = left.right
        left.right.parent = node

        left.right = node
        node.parent = left

建树


说是建树,其实就是树节点的插入操作
目标:让插入的节点同时满足二叉搜索树的要求和合法红黑树的性质

第一个目标很容易达成,就是找到该节点应该插入的位置然后把它塞进去

    def insert(self, value):
    	# Node(value, color, parent, left, right)
        insert_node = Node(value, NC.RED, None, self.nil, self.nil)
        curr_node = self.root

        while True:
            if curr_node.right == self.nil and value >= curr_node.value:
                curr_node.right = insert_node
                break
            if curr_node.left == self.nil and value <= curr_node.value:
                curr_node.left = insert_node
                break
            curr_node = curr_node.right if value >= curr_node.value else curr_node.left

        insert_node.parent = curr_node
        # self._adjust(insert_node)

接下来看看第二个目标,回顾一下之前的5条性质

  • 每个节点都是红色或者黑色的
  • 根节点是黑色
  • 叶节点是黑色
  • 如果一个节点是红色的,他的两个孩子都应该是黑色
  • 对每个结点,从该节点到其所有后代的叶子节点的简单路径上,包含相同数目的黑色节点

对应下来

  • 可以满足
  • 可以通过最后 root.color = NC.BLACK 完成
  • 已经在哨兵NIL中设置完成
  • 不一定
  • 通过给新插入的节点赋值为红色完成(不会增加黑色节点,但会collide第四条性质)

所以我们需要通过调整树结构和树节点颜色,来确保性质4的有效性,接下来将针对不同的树结构完成对合法红黑树的调整

在分析不同case之前,先给出一些定义

  • U:插入节点的叔节点 Uncle
  • P:插入节点的父节点 Parent
  • N:插入的节点 Node
  • G:插入节点的祖父节点 Grandparent

case1:根节点

这种情况比较简单,将该节点的颜色设置为黑色即可

case2:parent.color = BLACK

不需要下一步的处理了,如果父节点是黑色的,而插入的子节点默认为红色,不会影响性质4的有效性,无需调整树结构

所以!接下来的所有case当中,都会默认parent.color=RED!

case3:uncle.color = RED

直观来说,case3一定满足下图中4种情况中的一种(插入N前的树一定是valid的红黑树)

请添加图片描述

这种情况下,我们改变P、G、U的颜色,从而在维持黑高(性质5)的情况下,让红色节点和红色节点不相接(性质4)

在这里插入图片描述

但改变了G的颜色,使之成为RED节点之后,可能在G和G.parent之间存在性质4的冲突,所以将G看作新插入的节点N,重新判定当前情况

case4:uncle.color = BLACK

这种case下一定是下面4种状态之一,且一定出现在循环当中(因为P、G、U构成的子树在N插入前就不满足valid红黑树了)
也就是N的左右孩子一定非NIL,且有黑色节点

在这里插入图片描述
不难发现,对于右边两种情况来说,只需要对G做一次旋转就可以让这棵子树平衡;而左边的两种情况又可以通过旋转P达到和左边一样的结构

因此,我们对case4再分为2种情况,一种为三角结构(左侧2图),一种为线性结构(右侧2图)

case4.1: triangle structure

目标:变为线性结构,可以和右侧两图统一处理
解决方式:

  • 图1:左旋P,交换P和N
  • 图2:右旋P,交换P和N

case4.2: line structure

以第三张图为例,给出了处理方案

在这里插入图片描述

在处理后满足下一个处理的Node颜色为BLACK,不需要再次判定其与其parent的性质4合法性,因此可以结束树结构的调整

以上4种case的python代码如下

    def _adjust(self, node):
        # case1: node is root
        if node == self.root:
            node.color = NC.BLACK
            return

        # if node.parent is BLACK, the RBT is already valid
        # so grandparent is ALWAYS BLACK if continue processing
        if node.parent.color == NC.BLACK:
            return

        # pick up uncle, parent and grandparent(if parent is red, there must be grandparent)
        uncle, parent, grandparent, subtree_type = self._get_relative_nodes_and_subtree_type(node)

        # case2: uncle is red (implies grandparent is BLACK, parent is RED)
        if uncle.color == NC.RED:
            # change the color of uncle, parent and grand
            uncle.color = NC.BLACK
            parent.color = NC.BLACK
            grandparent.color = NC.RED
            return self._adjust(grandparent)

        # case3: uncle is black (this case includes "without uncle" cause nil node is black)
        # case3.1: deal with triangle tree type first
        if subtree_type == SBT.TRI_R:
            self._right_rotate(parent)
            subtree_type = SBT.LIN_R
            node, parent = parent, node
        if subtree_type == SBT.TRI_L:
            self._left_rotate(parent)
            subtree_type = SBT.LIN_L
            node, parent = parent, node

        # case3.2: deal with line tree type
        if subtree_type == SBT.LIN_R:
            self._left_rotate(grandparent)
        if subtree_type == SBT.LIN_L:
            self._right_rotate(grandparent)
        parent.color = NC.BLACK
        grandparent.color = NC.RED

    @staticmethod
    def _get_relative_nodes_and_subtree_type(node):
        parent = node.parent
        grandparent = parent.parent
        if parent.left == node:
            if grandparent.left == parent:
                subtree_type = SBT.LIN_L
                uncle = grandparent.right
            else:
                subtree_type = SBT.TRI_R
                uncle = grandparent.left
        else:
            if grandparent.left == parent:
                subtree_type = SBT.TRI_L
                uncle = grandparent.right
            else:
                subtree_type = SBT.LIN_R
                uncle = grandparent.left
        return uncle, parent, grandparent, subtree_type

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法刷题-链表-删除链表的倒数第N个节点

删除链表的倒数第N个节点 19.删除链表的倒数第N个节点思路其他语言版本 19.删除链表的倒数第N个节点 力扣题目链接 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#xff1f; 示例 1…

微服务_fegin

Feign服务调用 是客户端组件 ruoyi系统中Log\Auth\User用了远程服务调用&#xff0c;用工厂模式给他的报错加了层工厂类&#xff0c;return错误的时候重写了以下方法。 在ruoyi-common-core模块中引入依赖 <!-- SpringCloud Openfeign --><dependency><group…

springboot不香吗?为什么还要使用springcloud--各个组件基本介绍(Feign,Hystrix,ZUUL)

1.Feign负载均衡简介 1.1 Feign是什么 Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口&#xff0c;然后在上面添加注解&#xff0c;同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring…

07-根据Hutool工具的JWT实现单点登录功能

1、两种单点登录方案 1.1、使用token + Redis 实现单点登录(一般不用了) 1.2、使用JWT实现单点登录 2、 JWT单点登录的原理和JWT存在的问题及解决方案讲解 2.1、JWT结构 > Header 头部信息,主要声明了JWT的签名算法等信息。 > Payload 载荷信息,主要承载了各种声明并…

闲置APP小程序开发 你不喜欢的可能正是别人需要的

生活中我们常常会产生各种闲置物品&#xff0c;尤其是对于有宝宝的家庭来说&#xff0c;孩子小的时候可能会添置各种玩具、婴儿车或者是别的用品&#xff0c;随着孩子渐渐长大&#xff0c;这些东西都用不上了&#xff0c;但是扔了又觉得很可惜&#xff0c;留着又占地方&#xf…

给编程初学者写一篇简单的文章,快速查询本机ip,黑别人电脑的基基础

给编程小白写一篇简单的文章&#xff0c;快速查询本机ip 首先&#xff0c;按下win徽标键r键 &#xff08;先按win键再按r键&#xff09; 出现一个小窗口 在里面输入cmd 然后&#xff0c;你将得到一个cmd窗口 在里面输入ipconfig&#xff0c;然后按回车 就可以得到ip地址了 如…

【浅学 MyBatis 】

MyBatis 笔记记录 一、MyBatis基础1. MyBatis介绍及快速入门2. 相关API介绍2.1 Resources2.2 SqlSessionFactory&&SqlSessionFactoryBuilder2.3 SqlSession 3. 映射配置文件4. 核心配置文件4.1 规范写法4.2 参数和返回类型_起别名 5. 引入Log4j 二、MyBatis进阶1. 接口…

Mac环境Royal TSX 从入门使用代替X sheet

Royal TSX 是一款 macOS 下可用的远程连接软件&#xff0c;类似于 Windows 系统的 XShell 。免费版最多支持 10 个连接&#xff0c;对于个人开发而言&#xff0c;已经足够了 一、下载安装 Royal TS 官网&#xff1a;https://www.royalapps.com/ts/win/download 选择 Royal T…

Python Ploty学习: 最简单的Dash App

1 Ploty简介 示例来自官网A Minimal Dash App | Dash for Python Documentation | Plotly有改动 Ploty与matplotlib和seaborn相比&#xff0c;其核心优势在于可交互性&#xff0c;matplotlib和seaborn默认情况下不可交互&#xff0c;具备可交互性的图表在数据分析、展示方面更…

CLIP原理解读——大模型论文阅读笔记一

CLIP原理解读 一. 核心思想 通过自然语言处理来的一些监督信号&#xff0c;可以去训练一个迁移效果很好的视觉模型。 论文的作者团队收集了一个超级大的图像文本配对的数据集&#xff0c;有400 million个图片文本的配对&#xff0c; 模型最大用了ViT-large&#xff0c;提出了…

【JavaEE】使Cookie与Session失效-Servlet上传文件操作-优化表白墙

表白墙退出登录操作-表白墙注册上传头像登录显示头像功能 文章目录 【JavaEE】使Cookie与Session失效-表白墙退出登录操作-Servlet上传文件操作-表白墙注册上传头像登录显示头像功能1. Cookie与Session的删除1.1 表白墙页面增加登录出口1.2 点击链接退出登录1.3 测试 2. 上传文…

【Java线程池详解】—— 每天一点小知识

&#x1f4a7; J a v a 线程池 \color{#FF1493}{Java线程池} Java线程池&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f…

2023-6-10-第一式工厂方法模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

安卓与串口通信-数据分包的处理

前言 本文是安卓串口通信的第 5 篇文章。本来这篇文章不在计划内&#xff0c;但是最近在项目中遇到了这个问题&#xff0c;正好借此机会写一篇文章&#xff0c;在加深自己理解的同时也让大伙对串口通信时接收数据可能会出现分包的情况有所了解。 其实关于串口通信会可能会出现…

编译tolua——3、以pbc为例子,添加第三方库

目录 1、编译工具和环境说明 2、基础编译tolua 3、以pbc为例子&#xff0c;添加第三方库 4、更新luaJit 大家好&#xff0c;我是阿赵。 之前分享过怎样正常编译基础版本的tolua。这次用添加pbc为例&#xff0c;看看怎样往tolua里面添加其他的第三方库。知道了方法之后&#xf…

【郭东白架构课 模块二:创造价值】31 |节点六: 如何组织阶段性的价值交付?

你好&#xff0c;我是郭东白。上节课我们讲了为什么要做阶段性的价值交付&#xff0c;以及进入阶段性价值交付环节的准备工作。有了这些学习基础&#xff0c;这节课我们就可以进行阶段性价值交付了。 在交付的过程中&#xff0c;主要有三部分工作&#xff1a;目标分解、定义交…

SLAM十四讲——ch4实践(李群李代数)

视觉SLAM14讲----ch4的操作及避坑 一、ch4的实践的准备工作二、各个实践操作1. Sophus的基本使用方法2. 例子&#xff1a;评估轨迹误差 三、遇到的问题 一、ch4的实践的准备工作 确保已经有Sophus库&#xff0c;Sophus库是一个较好的李代数库。 注意&#xff1a; 开始时slamb…

MySQL 数据库实用指南:测试数据准备、SQL语句规范与基本操作

前言 欢迎来到小K的MySQL专栏&#xff0c;本节将为大家准备MySQL测试数据、以及带来SQL语句规范、数据库的基本操作的详细讲解~✨文末送书&#xff0c;小K赠书活动第二期 目录 前言一、准备测试数据二、SQL语句规范三、数据库的基本操作四、总结&#xff1a;文末赠书 一、准备测…

Linux之进程间通信——system V(共享内存、消息队列、信号量等)

文章目录 前言一、共享内存1.共享内存的基本原理2.共享内存的创建3.共享内存的控制参数返回值共享内存的内核数据结构 4.共享内存的关联参数 5.共享内存的去关联6.查看IPC资源7.查看共享内存8.删除共享内存 二、实现进程间通信&#xff08;代码&#xff09;三、共享内存的特点四…

【Newman+Jenkins】实施接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子&#xff0c;哈哈&#xff0c;开玩笑啦。。。别当真&#xff0c;简单地说Newman就是命令行版的Postman&#xff0c;查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行&#xff0c;把Postman界面化运…