二叉平衡树 之 红黑树 (手动模拟实现)
目录
1、红黑树的概念
2、红黑树的性质
3、红黑树节点的定义
4、红黑树的插入
5、红黑树验证
代码汇总
6、红黑树的删除(了解)
7、红黑树的应用
8、红黑树 VS AVL树
1、红黑树的概念
红黑树,就是一种特殊的二叉搜索树,每个节点会增加一个存储位来表示颜色,可以是红色或者黑色(Red/Black)
通过对于任何一条从根节点到叶子节点的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其路径长出两倍,因此是红黑树是接近平衡的
2、红黑树的性质
- 最长路径最多是最短路径的2倍
- 每个节点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子节点是黑色的【没有2个连续的红色节点】
- 对于每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
- 每个叶子结点都是黑色的【这里的叶子节点指的是空节点】
例如一个红黑树:
最长路径最多是最短路径的2倍:
3、红黑树节点的定义
public class RBTree {static class RBTreeNode {public RBTreeNode left;public RBTreeNode right;public RBTreeNode parent;public int val;public COLOR color;public RBTreeNode(int val) {this.val = val;//默认新创建的节点为红色this.color = COLOR.RED;}}public RBTreeNode root;}
其中COLOR是一个枚举类型:
public enum COLOR {RED,BLACK
}
4、红黑树的插入
同AVL树类似理解,他们都是在二叉搜索树上加上平衡限制条件,so?红黑树的插入也是分为两步:
- 按照二叉搜索树的规则插入新节点
- 检测新节点插入后,如果红黑树的性质遭到破坏,则对其进行修改
二叉搜索树的插入:
/*** 插入节点* @param val* @return*/public boolean insert(int val) {RBTreeNode node = new RBTreeNode(val);RBTreeNode cur = root;RBTreeNode parent = null;while (cur != null) {if(cur.val > val) {parent = cur;cur = cur.left;} else if(cur.val < val) {parent = cur;cur = cur.right;} else {return false;}}if(parent.val < val) {parent.right = node;} else {parent.left = node;}node.parent = parent;cur = node;//红黑树,调整颜色//...}
因为加入的新节点其颜色默认是红色,因此:
- 如果它的双亲节点的颜色是黑色,则没有违反红黑树的任何规则,就不需要对其调整;
- 如果新加入节点的双亲节点颜色为红色时,就违反了性质【红色节点不能连在一起】,需要进行调整
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
如果不懂左右单旋的伙伴们,可以回顾一下: http://t.csdn.cn/3PyVW
- 情况一:cur为红,p为红,g为黑,u存在且为红
- 如果g是根节点,调整完成后,需要将g改为黑色
- 如果g是子树,g一定有双亲,且g的双亲如果是红色,需要继续向上调整
代码:
parent.color = COLOR.BLACK;
uncle.color = COLOR.BLACK;
grandFather.color = COLOR.RED;
//继续向上修改
cur = grandFather;
parent = cur.parent;
- 情况2:cur为红,p为红,g为黑,u不存在/u为黑
代码:
rotateRight(grandFather);
grandFather.color = COLOR.RED;
parent.color = COLOR.BLACK;
- 情况三:cur为红,p为红,g为黑,u不存在/u为黑
代码:
//情况三:if(cur == parent.right) {rotateLeft(parent);RBTreeNode tmp = parent;parent = cur;cur = tmp;}//情况三变到了情况二//情况二:rotateRight(grandFather);grandFather.color = COLOR.RED;parent.color = COLOR.BLACK;
5、红黑树验证
类比于AVL树,红黑树的验证分两步:
- 检测是否满足二叉搜索树(中序遍历是否有序)
- 检测是否满足红黑树的各个性质
代码我就直接放在了代码汇总里~~~
代码汇总
/*** Created with IntelliJ IDEA.* Description:* User:龙宝* Date:2023-02-01* Time:12:45*/
enum COLOR {RED,BLACK
}
public class RBTree {static class RBTreeNode {public RBTreeNode left;public RBTreeNode right;public RBTreeNode parent;public int val;public COLOR color;public RBTreeNode(int val) {this.val = val;//默认新创建的节点为红色this.color = COLOR.RED;}}public RBTreeNode root;/*** 插入节点* @param val* @return*/public boolean insert(int val) {RBTreeNode node = new RBTreeNode(val);RBTreeNode cur = root;RBTreeNode parent = null;while (cur != null) {if(cur.val > val) {parent = cur;cur = cur.left;} else if(cur.val < val) {parent = cur;cur = cur.right;} else {return false;}}if(parent.val < val) {parent.right = node;} else {parent.left = node;}node.parent = parent;cur = node;//红黑树,调整颜色//...while (parent != null && parent.color == COLOR.RED) {RBTreeNode grandFather = parent.parent;//该引用不会是空的if(parent == grandFather.left) {RBTreeNode uncle = grandFather.right;if(uncle != null && uncle.color == COLOR.RED) {//情况一:parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandFather.color = COLOR.RED;//继续向上修改cur = grandFather;parent = cur.parent;} else {//uncle不存在 或者 uncle是黑色的//情况三:if(cur == parent.right) {rotateLeft(parent);RBTreeNode tmp = parent;parent = cur;cur = tmp;}//情况三变到了情况二//情况二:rotateRight(grandFather);grandFather.color = COLOR.RED;parent.color = COLOR.BLACK;}} else {//parent == grandFather.rightRBTreeNode uncle = grandFather.left;if(uncle != null && uncle.color == COLOR.RED) {parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandFather.color = COLOR.RED;//继续向上修改cur = grandFather;parent = cur.parent;} else {//情况三:if(cur == parent.left) {rotateRight(parent);RBTreeNode tmp = parent;parent = cur;cur = tmp;}//情况三变到情况二//情况二:rotateLeft(grandFather);grandFather.color = COLOR.RED;parent.color = COLOR.BLACK;}}}root.color = COLOR.BLACK;return true;}/*** 左单旋* @param parent*/private void rotateLeft(RBTreeNode parent) {RBTreeNode subR = parent.right;RBTreeNode subRL = subR.left;parent.right = subRL;subR.left = parent;if(subRL != null) {subRL.parent = parent;}RBTreeNode pParent = parent.parent;parent.parent = subR;if(root == parent) {root = subR;root.parent = null;}else {if(pParent.left == parent) {pParent.left = subR;}else {pParent.right = subR;}subR.parent = pParent;}}/*** 右单旋* @param parent*/private void rotateRight(RBTreeNode parent) {RBTreeNode subL = parent.left;RBTreeNode subLR = subL.right;parent.left = subLR;subL.right = parent;//没有subLRif(subLR != null) {subLR.parent = parent;}//必须先记录RBTreeNode pParent = parent.parent;parent.parent = subL;//检查 当前是不是就是根节点if(parent == root) {root = subL;root.parent = null;}else {//不是根节点,判断这棵子树是左子树还是右子树if(pParent.left == parent) {pParent.left = subL;}else {pParent.right = subL;}subL.parent = pParent;}}/*** 判断当前树 是不是红黑树* 得满足 红黑树的性质* @return*/public boolean isRBTree() {if(root == null) {//如果一棵树是空树,那么这棵树就是红黑树return true;}if(root.color != COLOR.BLACK) {System.out.println("违反了性质:根节点必须是黑色的!");}//存储当前红黑树当中 最左边路径的黑色的节点个数int blackNum = 0;RBTreeNode cur = root;while (cur != null) {if(cur.color == COLOR.BLACK) {blackNum++;}cur = cur.left;}//检查是否存在两个连续的红色节点 && 每条路径上黑色的节点的个数是一致的return checkRedColor(root) && checkBlackNum(root,0,blackNum);}/**** @param root* @param pathBlackNum 每次递归的时候,计算黑色节点的个数 0* @param blackNum 事先计算好的某条路径上的黑色节点的个数 2* @return*/private boolean checkBlackNum(RBTreeNode root,int pathBlackNum,int blackNum) {if(root == null) return true;if(root.color == COLOR.BLACK) {pathBlackNum++;}if(root.left == null && root.right == null) {if(pathBlackNum != blackNum) {System.out.println("违反了性质:每条路径上黑色的节点个数是不一样的!");return false;}}return checkBlackNum(root.left,pathBlackNum,blackNum) &&checkBlackNum(root.right,pathBlackNum,blackNum);}/*** 检查是否存在两个连续的红色节点* @param root* @return*/private boolean checkRedColor(RBTreeNode root) {if(root == null) return true;if(root.color == COLOR.RED) {RBTreeNode parent = root.parent;if(parent.color == COLOR.RED) {System.out.println("违反了性质:连续出现了两个红色的节点");return false;}}return checkRedColor(root.left) && checkRedColor(root.right);}public void inorder(RBTreeNode root) {if(root == null) {return;}inorder(root.left);System.out.print(root.val+" ");inorder(root.right);}}
6、红黑树的删除(了解)
找替代节点【替代节点的左子树、右子树一定有一个为空】
7、红黑树的应用
1. java集合框架中的:TreeMap、TreeSet底层使用的就是红黑树
2. C++ STL库 -- map/set、mutil_map/mutil_set
3. linux内核:进程调度中使用红黑树管理进程控制块,epoll在内核中实现时使用红黑树管理事件块
4. 其他一些库:比如nginx中用红黑树管理timer等
8、红黑树 VS AVL树
- 红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN)
- 红黑树不追求绝对平衡,只保证最长路径不超过最短路径的2倍,降低了插入时旋转的次数
- 在经常进行增删的结构中,红黑树性能比AVL树更优,并且红黑树的实现相对简单,所以实际中红黑树使用更多
好啦,下期见咯~
二叉平衡树 之 红黑树 (手动模拟实现)相关推荐
- 408数据结构学习笔记——二叉排序树、二叉平衡树、红黑树
目录 1.二叉排序树 1.1.二叉排序树的基本概念 1.2.二叉排序树的查找代码实现 1.3.二叉排序树的插入 1.4.二叉排序树的删除 1.5.二叉排序树的查找效率 1.6.二叉排序树的缺陷 2.平 ...
- 数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉 ...
- 算法导论习题—二叉搜索树、红黑树、区间树
算法基础习题-二叉搜索树.红黑树.区间树 1.二叉搜索树: 2.红黑树: 3.区间树: 1.二叉搜索树: 设 T T T是一棵二叉搜索树,其关键字互不相同;设 x x x是一个叶结点, y y y为其 ...
- 二叉搜索树,和红黑树,
二叉搜索树 class node():def __init__(self,a):self.val=aself.left=Noneself.right=None #当函数写起来复杂时候,考虑给函数加变量 ...
- 红黑树 平衡二叉搜索树_红黑树:自我平衡的二叉搜索树,并举例说明
红黑树 平衡二叉搜索树 什么是红黑树? (What is a Red-Black Tree?) Red-Black Tree is a type of self-balancing Binary Se ...
- 二叉搜索树、平衡二叉搜索树和红黑树
文章目录 一. 二叉搜索树(Binary Sort Tree) 二. 二叉平衡搜索树(AVL) 三. 红黑树 一. 二叉搜索树(Binary Sort Tree) 二叉搜索树,又称为二叉排序树(二叉查 ...
- 数据结构->二叉搜索树->平衡二叉搜索树->红黑树的C++实现
节点的定义: RBTreeNode.h // // Created by 24588 on 2022/1/12. //#ifndef TEST_RBTREENODE_H #define TEST_RB ...
- 二叉搜索树BST红黑树
二叉搜索树基础知识 提起红黑树咱们肯定要先说说这个二叉搜索树(BST) 二叉搜索树又叫二叉查找树,二叉排序树:它具有以下特点: 如果它的左子树不为空,则左子树上结点的值都小于根结点. 如果它的右子树不 ...
- 二叉搜索树:红黑树的原理和实现
目录 前言 1. 红黑树的概念 2. 红黑树的性质 3. 红黑树的定义 4. 红黑树的插入操作 5. 红黑树的验证 6. 红黑树和AVL树的比较 7. 红黑树的应用 前言
最新文章
- 基于MySQL数据库下亿级数据的分库分表
- 网络爬虫基本原理(一)
- 用Windows Server实现软件定义存储之存储空间直连
- 蒙克:云计算安全问题被夸大
- linux怎么用jconsole_jconsole监控linux系统的jvm使用
- 生活在任务栏的猫, CPU使用率越高它就跑的越快
- leetcode 1356. 根据数字二进制下 1 的数目排序(排序)
- JSON C# Class Generator ---由json字符串生成C#实体类的工具
- ethtool的内核流程跟踪
- 学习Maven之Maven Surefire Plugin(JUnit篇)
- Rust:阴阳谜题输出
- 电脑前面耳机插孔没声音,后面有声音
- mysql 导出 客户端_Web基础配置篇(四): Mysql的配置及使用
- 搭建一个vue项目完整步骤及详细讲解
- 5W无线充发射IC芯片方案XPM7105、XPM7305 无线充SOC芯片
- RJ45接头 与 RJ48 接头
- 跨专业考研应该怎么做?
- 51 单片机 程序 测量占空比 测量频率 频率计 占空比 proteus
- 3.利用PageRank(重启随机游走)预测蛋白质相互作用
- 第1期-软件测试-简历-正确编写测试简历的姿势
热门文章
- js html body onload,动态添加页面body OnLoad事件的简单js代码
- 视频剪辑用计算机,用于视频剪辑的笔记本电脑,我们都有哪些选择
- 我的世界mod开发(6)无敌的护甲
- kinect 学习笔记一
- Adobe_Acrobat_Pro_DC_2022.003.20314 下载安装
- 苹果和虫子c语言编程题,虫子吃苹果:每天10分钟,锻炼少儿编程计算思维
- 计算机网络 理论复习概括
- 学生学籍管理系统_管理员登陆对学生的信息进行操作
- vue使用three.js加载.FBX模型文件
- 外网如何连接学校服务器