红黑树原理及代码实现(一)

article/2025/11/3 15:42:23

红黑树简介

红黑树是一种自平衡的二叉查找树,它的统计性能要优于平衡二叉树,因此在很多地方都有应用,例如Java中的TreeMap,HashMap等数据结构都使用到了红黑树。红黑树对很多人来说熟悉而陌生,熟悉则是因为知道红黑树在很多场景中有使用,但又对其原理很陌生或者一知半解。今天就来剖析一下红黑树原理

红黑树是一种二叉查找树,但是它在二叉查找树基础上增加了着色和相关的性质使得其相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。红黑树具备以下5个性质:

1.每个节点要么是黑色,要么是红色

2.根节点是黑色

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

4.每个红色节点的子节点一定是黑色,不能有两个红色节点相连

5.任意一节点到每个叶子节点的路径包含的黑节点数量相同。俗称黑高

以上5个性质是硬性规定,记忆即可

红黑树的操作

插入节点

旋转

在讲插入节点之前,先普及一下树的旋转,如果学习过平衡树的程序员应该都会大概知道旋转的操作。旋转分为左旋和右旋,下面两个动图很形象的解释了左旋和右旋:

左旋
在这里插入图片描述
左旋就是父节点退化成其右子节点的的左子节点,而原本的右子节点升级为父节点,一般有两个步骤:

1.当前节点E与其右子节点S的左子节点相连,作为当前节点E的右子节点

2.用当前节点E的右子节点替换E的位置,E变成S的左子节点

右旋

在这里插入图片描述
右旋就是父节点退化成其左子节点的右子节点,而原本的左子节点升级为父节点,一般有两个步骤:

1.将当前节点S与其左子节点的右子节点相连,作为当前节点S的左子节点

2.用当前节点S的左子节点E替换S的位置,S变成E的右子节点

在插入新节点之前,需要明确两个共识:

1.插入新节点有可能会破坏原红黑树的平衡,因此需要对新的树进行调整变为红黑树;

2.插入的节点必须是红色,因为插入黑色节点必然会导致一条路径上黑色节点数量+1,破坏红黑树的黑色平衡

现在我们来分析一下插入节点后可能面临的几种情况:

  1. 如果树为空,插入新节点,需要将新增节点赋给根节点,并将节点变成黑色
  2. 如果新节点的父节点为黑色,则不需要进行任何平衡操作,因为新增的红色节点没有破坏红黑树的性质4,5
  3. 如果新节点的父节点为红色,父节点有可能是爷爷节点的左子节点和右子节点两种情况:
    3.1. 父节点是爷爷节点的左子节点,有如下3中情形:
    3.1.1. 叔叔节点也是红色(新节点是父节点的左还是右子节点不影响):
    解决方案:将父节点和叔叔节点都变成黑色,爷爷节点变成红色,然后将爷爷节点当成新插入节点,继续同样的操作,直到当前节点变成根节点,然后将根节点变成黑色
    在这里插入图片描述
    3.1.2.新增节点是父节点的左子节点,但叔叔节点是黑色(简称“左左”)
    解决方案:将父节点变成黑色,爷爷节点变成红色,然后将爷爷节点右旋
    在这里插入图片描述
    3.1.3.新增节点是父节点的右子节点,叔叔节点是黑色(简称“左右”)
    解决方案:对父节点进行左旋,就变成了和3.1.2一样的树,按照3.1.2进行再处理
    在这里插入图片描述
    3.2.父节点是爷爷节点的右子节点,与上述一样也有3中情形:
    3.2.1.叔叔节点是红色
    解决方案与3.1.1一样
    3.2.2.新增节点是父节点的右子节点,叔叔节点为黑色(简称“右右”)
    解决方案:将父节点变为黑色,爷爷节点变为红色,然后将爷爷节点左旋
    在这里插入图片描述
    3.2.3.新增节点是父节点的左子节点,叔叔节点为黑色(简称“右左”)
    解决方案:将父节点进行右旋,就变成了3.2.2一样的树,按照3.2.2进行再处理
    在这里插入图片描述

代码处理

定义红黑树类RBTree

public class RBTree<K extends Comparable<K>, V> {private static final boolean RED = false;private static final boolean BLACK = true;/*** 根节点引用*/private RBNode root;
}

定义节点类RBNode,这里直接定义在RBTree中作为静态内部类

static class RBNode<K extends Comparable<K>, V> {K key;V value;RBNode left;RBNode right;RBNode parent;boolean color = BLACK;public void setValue(V value) {this.value = value;}public V getValue() {return value;}
}

左旋操作:

/*** 左旋*       p.parent             r.parent*        丨                   丨*        p     -------->      r*      /   \                /  \*   p.left  r              p    y*         / \            /  \*        x   y       p.left  x* p:当前操作节点* 1.将p的右子节点更新为r的左子节点,r的左子节点的父节点更新为p* 2.r替换p的位置,即r的父节点更新为p的父节点,p的父节点的子节点更新为r* 3.r的左子节点更新为p,p的父节点更新为r* 如果当前节点为null,不操作** @param p*/
private void rotateLeft(RBNode p) {if (p != null) {RBNode r = p.right;// 1.1 将p的右子节点更新为r的左子节点p.right = r.left;// 1.2 r的左子节点的父节点更新为pif (r.left != null) {r.left.parent = p;}// 2.1 r的父节点更新为p的父节点r.parent = p.parent;// 2.2 p的父节点的子节点更新为rif (p.parent == null) {root = r;} else if (p.parent.left == p) {p.parent.left = r;} else {p.parent.right = r;}// 3 r的左子节点更新为p,p的父节点更新为rr.left = p;p.parent = r;}
}

右旋操作:

/*** 右旋*       p.parent             r.parent*        丨                   丨*        p     -------->      l*      /   \                /  \*     l   p.right         x     p*   / \                       /  \*  x   y                     y    p.right*  p:当前操作节点*  1.将p的左子节点更新为l的右子节点,l的右子节点的父节点更新为p*  2.l替换p的位置,即l的父节点更新为p的父节点,p的父节点更新为l*  3.l的右子节点更新为p,p的父节点更新为l*  如果当前节点为null,不操作* @param p*/
private void rotateRight(RBNode p) {if (p != null) {RBNode l = p.left;p.left = l.right;if (l.right != null) {l.right.parent = p;}l.parent = p.parent;if (p.parent == null) {root = l;} else if (p.parent.left == p) {p.parent.left = l;} else {p.parent.right = l;}l.right = p;p.parent = l;}
}

插入节点后的调整动作:

/*** 对插入新节点后的二叉树进行调整转成红黑树** @param x*/
private void fixAfterInsertion(RBNode x) {x.color = RED; // 新插入的节点必须为红色// 不需要任何调整的情况:新节点的父节点为黑色// 如果新节点就是root节点,只需变色即可// 最为简单的情况,暂不予考虑while (x != root && x.parent.color == RED) {if (x.parent == x.parent.parent.left) { // 新节点的父节点是祖父节点的左子节点RBNode y = x.parent.parent.right; // 叔叔节点if (colorOf(y) == RED) { // 叔叔节点也为红色// 将父节点和叔叔节点变为黑色,祖父节点变为红色,再把祖父节点当做新节点依次雷同操作,直到祖父节点为root或黑色setColor(x.parent, BLACK);setColor(y, BLACK);setColor(x.parent.parent, RED);x = x.parent.parent;} else { // 叔叔节点为黑色// 需要判断新节点是父节点的左子节点还是右子节点if (x == x.parent.right) { // 左右,先父节点左旋,之后与左左一致做法x = x.parent;rotateLeft(x);}// 左左setColor(x.parent, BLACK);setColor(x.parent.parent, RED);// 祖父节点右旋rotateRight(x.parent.parent);}} else { // 新节点的父节点是祖父节点的右子节点RBNode y = x.parent.parent.left;if (colorOf(y) == RED) {setColor(x.parent, BLACK);setColor(y, BLACK);setColor(x.parent.parent, RED);} else {if (x == x.parent.left) {x = x.parent;rotateRight(x);}setColor(x.parent, BLACK);setColor(x.parent.parent, RED);rotateLeft(x.parent.parent);}}}
}

插入新节点

/*** 插入节点* @param node*/
public void insert(RBNode node) {if (node == null) {return;}if (root == null) { // 初始化rootroot = node;root.color = BLACK;return;}RBNode x = root;RBNode p = null;// p用来记录x的父节点int cmp;do {p = x;cmp = node.key.compareTo(x.key);// cmp > 0:新节点比当前节点大;cmp < 0:新节点比当前节点小;否则相等if (cmp < 0) {// 往当前结点的左子节点遍历x = x.left;} else if (cmp > 0){ // 往右子节点遍历x = x.right;} else {x.setValue(node.getValue());}} while (x != null);node.parent = p;if (cmp < 0) {p.left = node;}else {p.right = node;}// 插入新节点后,进行调整转成红黑树fixAfterInsertion(node);
}

以上只完成了红黑树新增节点的原理解释以及代码实现,删除节点操作会放在后面的文章中讲解。红黑树的源码可在JDK源码TreeMap中查看,当然如果想看有注释版的,也可以到我的github查看:https://github.com/ithushuai/tree


http://chatgpt.dhexx.cn/article/2XnSY9v6.shtml

相关文章

图解红黑树原理

一.红黑树的性质&#xff08;一种自平衡的二叉查找树&#xff09; 1. 根节点是黑色。 2. 节点是红色或黑色 3. 叶子节点都是黑色的空节点&#xff08;NIL节点&#xff09; 意思就是末尾一排是黑色空节点&#xff08;可全部省略&#xff09; &#xff08;叶子节点意思是没有子…

红黑树原理和算法详细介绍

目录 1 R-B Tree简介 2 红黑树的时间复杂度和相关证明 3 红黑树的基本操作 1. 左旋 2. 右旋 3. 添加 4.1 删除 1 R-B Tree简介 R-B Tree&#xff0c;全称是Red-Black Tree&#xff0c;又称为“红黑树”&#xff0c;它一种特殊的二叉查找树。红黑树的每个节点上都有存储…

HashMap底层红黑树原理(超详细图解)+手写红黑树代码

在看完了小刘老师和黑马的源码视频之后,我整理了一篇HashMap的底层源码文章&#xff0c;学海无涯&#xff0c;这几天看了对红黑树的讲解&#xff0c;故将其整理出来 HashMap底层源码解析上 HashMap底层源码解析下 视频链接 小刘老师讲源码 传智播客-黑马程序员 文章目录 树结…

红黑树原理及java实现

红黑树 红黑树规则特点&#xff1a; 节点分为红色或者黑色&#xff1b;根节点必为黑色&#xff1b;叶子节点都为黑色&#xff0c;且为null&#xff1b;连接红色节点的两个子节点都为黑色&#xff08;红黑树不会出现相邻的红色节点&#xff09;&#xff1b;从任意节点出发&…

红黑树原理及旋转

红黑树&#xff0c;本质上来说就是一棵二叉查找树 但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡 保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n) 但它是如何保证一棵n个结点的红黑树的高度始终保持在h logn的呢&#xff1f;这就引出了红黑…

HashMap红黑树原理分析

近期学习了 HashMap 实现原理&#xff0c;这篇咱们了解一下红黑树的设计&#xff0c;相比 jdk1.7 的 HashMap 而言&#xff0c;jdk1.8最重要的就是引入了红黑树的设计&#xff0c;当hash表的单一链表长度超过 8 个的时候&#xff0c;链表结构就会转为红黑树结构。 01、故事的起…

HashMap源码学习:红黑树原理详解

文章导航 HashMap源码学习&#xff1a;红黑树原理详解 HashMap源码学习&#xff1a;JDK1.8版本源码解析 目录 文章导航前言概述红黑树的特性变色平衡右旋平衡左旋平衡 正文红黑树平衡方法&#xff1a;balanceInsertion红黑树左旋方法&#xff1a;rotateLeft红黑树右旋方法&…

红黑树原理讲解

红黑树原理讲解 一、红黑树的性质二、红黑树的3种变化策略&#xff1f;&#xff08;为满足红黑树性质&#xff09;1. 改变颜色2. 左旋3. 右旋 三、红黑树的插入情景1&#xff1a;红黑树为空树情景2&#xff1a;插入节点的key已经存在情景3&#xff1a;插入节点的父节点为黑色情…

红黑树原理详解

红黑树原理详解 红黑树的旋转红黑树上结点的插入红黑树上结点的删除 参考&#xff1a; 教你初步了解红黑树 红黑树(一)之 原理和算法详细介绍 红黑树定义&#xff1a; (1) 每个节点或者是黑色&#xff0c;或者是红色。 (2) 根节点是黑色。 (3) 每个叶子节点是黑色。 [注意&…

红黑树原理

因为看的时候写在了纸上&#xff0c;于是直接扫描的&#xff0c;效果不太好。

HashMap红黑树原理解析

HashMap红黑树原理解析 定义&#xff1a; 简单来说红黑树是一种近视平衡二叉查找树&#xff0c;主要优点是”平衡”&#xff0c;即左右子树高度几乎一致&#xff0c;以此来防止树退化为链表&#xff0c;通过这种方式来保障查找的时间复杂度为log(n)。 下面先主要提一下红黑树…

红黑树的原理及实现

红黑树的原理及实现 一、什么是红黑树二、定义红黑树三、左旋和右旋四、红黑树插入节点 一、什么是红黑树 红黑树是一种特定类型的二叉树&#xff0c;它是在计算机科学中用来组织数据比如数字的块的一种结构。 红黑树是一种平衡二叉查找树的变体&#xff0c;它的左右子树高差有…

【图文详解】彻底了解红黑树底层实现原理

红黑树定义 红黑树&#xff08;Red Black Tree&#xff09; 是一种自平衡二叉查找树&#xff0c;是在计算机科学中用到的一种数据结构&#xff0c;典型的用途是实现关联数组。 红黑树是一种特化的AVL树&#xff08;平衡二叉树&#xff09;&#xff0c;都是在进行插入和删除操…

红黑树(一)之 原理和算法详细介绍

概要 目录1 红黑树的介绍2 红黑树的应用3 红黑树的时间复杂度和相关证明4 红黑树的基本操作(一) 左旋和右旋5 红黑树的基本操作(二) 添加6 红黑树的基本操作(三) 删除 作者&#xff1a;Sky Wang 于 2013-08-08 概述&#xff1a;R-B Tree&#xff…

数据结构 | 【红黑树】图解原理

今天我们要说的红黑树就是就是一棵非严格均衡的二叉树&#xff0c;均衡二叉树又是在二叉搜索树的基础上增加了自动维持平衡的性质&#xff0c;插入、搜索、删除的效率都比较高。红黑树也是实现 TreeMap 存储结构的基石。 红黑树就是非严格均衡的二叉搜索树。 一、红黑树规则特…

红黑树原理简单解析

一、红黑树为什么会出现呢&#xff1f; 是因为二叉搜索树有可能会出现极端的情况&#xff0c;就是只有一侧有数据&#xff0c;那这样的话就会降级为链表。后来出现了平衡二叉树&#xff0c;但是由于强制平衡所导致付出的代价比较高昂&#xff0c;所以黑红树出现了。 二、简介…

laravel框架的下载与安装

首先我们先访问这个网址https://github.com/laravel/laravel 可以使用git克隆 或者直接下载安装包到自己的www目录下解压&#xff0c;此时访问localhost下的laravel目录&#xff0c; 此时会报没有vendor文件夹的一个错&#xff0c; 如果你已经下载了composer 在该文件夹目录下…

Laravel

Laravel的安装 1.确认composer已经安装 2.配置homestead.yaml文件sites&#xff1a;- map: laravel.xyz//域名配置to: /home/vagrant/code/laravel/public//入口文件路径 databases&#xff1a;//数据库- laravel 3.用秘钥登录homestead 4.更换中国镜像&#xff1a; composer…

Laravel下载文件及文档

2019独角兽企业重金招聘Python工程师标准>>> Laravel学院提供的相关资源下载 中文文档 Laravel 5.6 中文文档&#xff1a;PDF&#xff08;兼容 5.5 文档&#xff09; Laravel 5.3 中文文档&#xff1a;CHM | PDF Laravel 5.2 中文文档&#xff1a;CHM | PDF Laravel…

Laravel-admin的安装步骤

1、先确保是否安装composer&#xff0c;laravel及其版本并切换到阿里镜像 composer -v 查看composer的版本 php artisan 查看laravel的版本 扩展说明下&#xff1a;如果已经安装好composer&#xff0c;也可以通过 Composer 安装 Laravel 安装器 命令如下&#xff1a;composer…