图解TreeMap的红黑树平衡操作fixAfterInsertion(),接着手撕红黑树添加节点

article/2025/9/21 1:18:15

一、前言

啥也不想说,就卷、卷技术;手撕红黑树搞起。

1、红黑树简介

红黑树就是一种平衡的二叉查找树,其有五个特点:

1.每个节点要么是红⾊,要么是⿊⾊;
2. 根节点⼀定是⿊⾊的;
3. 每个叶⼦节点⼀定是⿊⾊的NULL节点;
4. 如果⼀个节点是红⾊,那么它的左右⼦节点⼀定都是⿊⾊的;
5. 从任意⼀个节点到叶⼦节点,所经过的⿊⾊节点的数量⼀样多;

在这里插入图片描述

2、TreeMap简介

TreeMap 继承于AbstractMap ,其是一个 有序的key-value集合,内部基于 红黑树 实现;
TreeMap 根据 其key的自然顺序进行排序,或者在构造方法中指定 Comparator 进行排序;
TreeMap的基本操作 containsKey()、get()、put() 和 remove() 的时间复杂度是 log(n)。
另外,TreeMap是非同步 的。 它的迭代器是fail-fast 的。

二、put()方法

1、put操作实现原理

  1. 如果树为null,则构建一个TreeMapEntry设置为当前的root;
  2. 排序方式优先使用自定义比较器,找到要插入节点的父节点,然后插入;
  3. 执行fixAfterInsertion(e)方法维护红黑树的平衡
public V put(K key, V value) {Entry<K,V> t = root;if (t == null) {compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;// split comparator and comparable pathsComparator<? super K> cpr = comparator;if (cpr != null) {do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}else {if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;fixAfterInsertion(e);size++;modCount++;return null;
}

我们重点看一下红黑树的平衡是如何维护的

2、维护红黑树的平衡 – fixAfterInsertion()

建议大家细细拼,这一块还挺绕!!

新插入的节点颜色默认为红色

平衡操作流程如下:

  1. 当节点不是跟节点且节点的父节点颜色为红色时进行while循环操作。
  1. 节点的父节点为爷爷节点的左子节点时;取爷爷节点的右子节点;
  • 1)爷爷节点的右子节点颜色为红色时;
    将爷爷节点的颜色改为红色,父节点和父节点的兄弟节点置为黑色;
    在这里插入图片描述
    在这里插入图片描述
  • 2)否则:
    1、如果x是其父节点的右子节点:令当前节点为其父节点,接着执行左旋转操作;
    在这里插入图片描述
    在这里插入图片描述
    2、将x的父节点和爷爷节点的颜色对换。然后对爷爷节点进行右旋转;
    在这里插入图片描述
    在这里插入图片描述
  1. 节点的父节点为爷爷节点的右子节点时,取爷爷节点的左孩子;
  • 1)当爷爷节点的左子节点颜色为红色时;
    父节点和父节点的兄弟节点设置为黑色,爷爷节点设置为红色;
    将爷爷节点作为新插入的节点x,遍历调整;
    在这里插入图片描述
    在这里插入图片描述
  • 2)否则:
    1、如果x是其父亲的左孩子:令x为其父节点,然后进行右旋操作;
    在这里插入图片描述
    在这里插入图片描述
    2、将父节点设置为黑色,爷爷节点设置为红色,然后以爷爷节点为旋转点进行左旋转;
    在这里插入图片描述
    在这里插入图片描述

最后将根节点的颜色设置为黑色;
在这里插入图片描述

private void fixAfterInsertion(Entry<K,V> x) {x.color = RED;while (x != null && x != root && x.parent.color == RED) {if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {Entry<K,V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == rightOf(parentOf(x))) {x = parentOf(x);rotateLeft(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateRight(parentOf(parentOf(x)));}} else {Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == leftOf(parentOf(x))) {x = parentOf(x);rotateRight(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateLeft(parentOf(parentOf(x)));}}}root.color = BLACK;
}

图解解决,我们接着手撕个红黑树的添加节点流程。

三、手撕红黑树添加节点

public class RedBlackTree<K extends Comparable<K>, V> {public static void main(String[] args) {RedBlackTree rdTree = new RedBlackTree();rdTree.add(1, 1);rdTree.add(3, 1);rdTree.add(2, 1);RedBlackTree.TreeNode root = rdTree.root;System.out.println(root);}/*** 判断节点指向父节点的连线的颜色 Red<-->true, Black<-->false*/private static final boolean RED = true;private static final boolean BLACK = false;/*** 树节点*/class TreeNode {public K key;public V value;public TreeNode left;public TreeNode right;public boolean color;public TreeNode(K key, V value) {this.key = key;this.value = value;this.left = null;this.right = null;this.color = RED;}}private TreeNode root;private int size;public RedBlackTree() {root = null;size = 0;}/*** 判断一个树节点是否是红节点。** @param node 树节点*/public boolean isRed(TreeNode node) {if (node == null) {return BLACK;}return node.color;}public void add(K key, V value) {root = add(root, key, value);root.color = BLACK;}public TreeNode add(TreeNode node, K key, V value) {if (node == null) {size++;return new TreeNode(key, value);}// 1.往树中添加节点if (key.compareTo(node.key) < 0) {node.left = add(node.left, key, value);} else if (key.compareTo(node.key) > 0) {node.right = add(node.right, key, value);} else {node.value = value;}// 2.维护红黑树的结构// 2.1 如果node节点的右子节点为红色的,而左子节点不是红色的,左旋转if (isRed(node.right) && !isRed(node.left)) {node = leftRotate(node);}// 2.2 如果node节点的左子节点、左子节点的左子节点是红色的,右旋转if (isRed(node.left) && isRed(node.left.left)) {node = rightRotate(node);}// 2.3 如果node节点的左子节点和右子节点都是红节点,则颜色翻转if (isRed(node.left) && isRed(node.right) && !isRed(node)) {flipColor(node);}return node;}/*** 左旋转* node                      x* /  \         左旋转       /  \* T1  x      --------->  node  T3* / \                    / \* T2 T3                 T1  T2*/private TreeNode leftRotate(TreeNode node) {TreeNode x = node.right;node.right = x.left;x.left = node;x.color = node.color;node.color = RED;return x;}/*** 右旋转* node                          x* /  \         右旋转          /  \* x   T3      --------->    T1   node* / \                            /  \* T1 T2                         T2  T3*/private TreeNode rightRotate(TreeNode node) {TreeNode x = node.left;node.left = x.right;x.right = node;x.color = node.color;node.color = RED;return x;}/*** 颜色翻转* 如果一个节点的颜色为黑色,而它左右子节点的颜色为红色。* 则将其的颜色置为红色,其子节点的颜色置为黑色。*/private void flipColor(TreeNode node) {node.color = RED;node.left.color = BLACK;node.right.color = BLACK;}
}

只要我们技术足够卷、没有hold不住的技术面,奥利给!


http://chatgpt.dhexx.cn/article/RbmM8k0g.shtml

相关文章

有人在jdk源码里下毒【class.newInstance() bug复现】

如图 在用反射的时候&#xff0c;发现这个方法被idea划横杠了 稍加思索后发现是这方法从jdk9开始弃用了&#xff0c;倒不影响使用&#xff0c;对象还是能正常射出来&#xff0c;就是看着很难受 &#xff08;最近刚把本地开发机从8升到11&#xff0c;难怪&#xff09; 说下我…

红黑树理解以及Java实现

红黑树本身并不复杂&#xff0c;只是在插入删除的时候情况比较多&#xff0c;如果强行记忆的话会显得比较困难&#xff0c;而且容易忘记。所以以前对红黑树一直没有很好的掌握。恰好这次借着复习数据结构的机会&#xff0c;静下心来仔细的学习了一下红黑树&#xff0c;并用Java…

Java 9 vs Java 8:引入模块化和JShell的全面升级

Java 9 是 Java 语言的一个重大版本升级&#xff0c;带来了许多新的特性和改进。 在本篇博客中&#xff0c;我将为您介绍 Java 9 的一些重要特性。 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; 目录 一、模块化二、JShell三…

HashMap 在 JDK 1.8 后新增的红黑树结构

读完本文你将了解到&#xff1a; 点击查看 Java 集合框架深入理解 系列 - - 乾杯传统 HashMap 的缺点HashMap 在 JDK 18 中新增的数据结构 红黑树HashMap 中关于红黑树的三个关键参数HashMap 在 JDK 18 中新增的操作桶的树形化 treeifyBinHashMap 在 JDK 18 中新增的操作 红黑树…

epoll底层红黑树使用部分源码剖析:为什么使用红黑树以及如何使用红黑树

我们知道epoll的底层使用了红黑树来管理文件描述符&#xff0c;为什么会选择红黑树这种结构呢&#xff1f; 以下是个人理解&#xff1a; epoll和poll的一个很大的区别在于&#xff0c;poll每次调用时都会存在一个将pollfd结构体数组中的每个结构体元素从用户态向内核态中的一…

HashMap在jdk1.8为何引入了红黑树?

原创不易,麻烦点个关注,点个赞,谢谢各位。 二叉查找树 二叉查找树,也称有序二叉树(ordered binary tree),或已排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若任…

从电路的角度理解特征阻抗

传输显得特征阻抗不是真实的电阻&#xff0c;微波技术课程会从波的角度描述特征阻抗&#xff0c;这次试图从电路的角度来理解 无损传输线是分布的L C 网络&#xff0c;假设是无限长传输线 从a,b两点看入的阻抗是相等的&#xff0c;所以可以简化成下图&#xff1a; 化简可得 这…

同轴电缆阻抗总结(电阻、阻抗、特性阻抗)

文章目录 同轴电缆电阻、阻抗、特性阻抗电阻阻抗&#xff08;Impedance&#xff09;特性阻抗 总结 同轴电缆 同轴电缆是一种电线及信号传输线&#xff0c;一般是由四层物料造成&#xff1a;最内里是一条导电铜线&#xff0c;线的外面有一层塑胶&#xff08;作绝缘体、电介质之…

传输线阻抗方程的推导

在传输线理论中&#xff0c;当一段特征阻抗为 Z 0 Z_0 Z0​ 的传输线的终端连接了一个阻抗为 Z L Z_L ZL​ 的负载时&#xff0c;看向这段传输线的输入阻抗 Z i n Z_{in} Zin​ 将不再是 Z 0 Z_0 Z0​。 传输线阻抗方程 (Transmission Line Impedance Equation) 就是计算…

PCB阻抗计算

阻抗匹配是指在能量传输时&#xff0c;要求负载阻抗要和传输线的特征阻抗相等&#xff0c;此时的传输不会产生反射&#xff0c;这表明所有能量都被负载吸收了。反之则在传输中有能量损失。在高速PCB设计中&#xff0c;阻抗的匹配与否关系到信号的质量优劣&#xff0c;下面简单介…

特征阻抗和阻抗匹配_没有诸如对象关系阻抗不匹配之类的东西

特征阻抗和阻抗匹配 过去十年来&#xff0c;ORM的许多批评都错了这一点&#xff0c;因为它不准确。 到本文结尾&#xff0c;我们将得出以下结论&#xff1a; 关系&#xff08;数据&#xff09;模型和面向对象的模型之间没有显着差异 如何得出这个结论&#xff1f; 继续阅读&a…

传输线特征阻抗计算

一直有很多人问我阻抗怎么计算的. 人家问多了,我想给大家整理个材料,于己于人都是个方便.如果大家还有什么问题或者文档有什么错误,欢迎讨论与指教! 在计算阻抗之前,我想很有必yi要理解这儿阻抗的意义 传输线阻抗的由来以及意义 传输线阻抗是从电报方程推导出来(具体可以查询微…

PCB特征阻抗计算

见教程&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1V4UbEoKfMD1bilwu-Qwdyg 密码&#xff1a;ml6t

射频特征阻抗

Characteris Impendance(特性阻抗&#xff0c;也称为‘特征阻抗’)是我们经常看到并使用自己的术语之一&#xff0c;但非常模糊且难以解释。以下是来自几个不同来源的Characteris Impendance(特性阻抗)的一些定义。 &#xff08;如果您检查10个不同的来源&#xff0c;您会看到1…

高速PCB的特征阻抗设计

我们在高速PCB设计当中,经常对高速信号线做特征阻抗控制来优化信号质量。那特征阻抗是什么东西呢? 1.传输线原理 介绍特征阻抗之前,我们复习下《信号完整性视频》介绍的传输线基本原理。如下图左边是低频电路采用集总参数的RLGC模型,右边高频电路采用分布参数的RLGC模型。…

传输线的波阻抗与特征阻抗

以上是时域方程&#xff0c;而我们的“波阻抗”是定义在频域下的&#xff08;正弦激励&#xff09;。 1&#xff09;“相电压/电流”的第一、二项分别代表了前向传输、反向传输分量&#xff1b; 2&#xff09;前向传输和反向传输分量两者无必然联系。 补充修改&#xff1a; 1&…

PCB寄生参数和特征阻抗

1、微带线Microstrip 相同情况下&#xff0c;PCB板厚H越厚&#xff08;影响很大&#xff09;&#xff1a; 特征阻抗越大&#xff08;H↑ > ln()↑ > Z0↑&#xff09;传输延时几乎不变&#xff08;与H无关&#xff09;寄生电感越大&#xff08;H↑ > ln()↑ > L…

传输线的特征阻抗

要理解特征阻抗首先要建立一个模型。传输线零阶模型 在这个模型中&#xff0c;每一个步长是△X&#xff0c;单位长度的电容为CL&#xff0c;所以每个步长的电容 CCL*△X 然后我们根据电荷量 QU*CI*t &#xff0c;电流I Q / t C * U / t&#xff0c;其中t △X / v得到 电流 …

阻抗,特征阻抗与等效阻抗

目录 一、阻抗 二、 特征阻抗 三、等效阻抗 射频的黄金三角之一就是阻抗&#xff0c;我们在射频设计中&#xff0c;会经常与阻抗打交道&#xff0c;比如特征阻抗&#xff0c;负载阻抗&#xff0c;阻抗匹配等等。更多的时候&#xff0c;我们所设计的射频电路就是一个阻抗匹配…

特性阻抗介绍

特性阻抗:又称“特征阻抗”,它不是直流电阻,属于长线传输中的概念。在高频范围内,信号传输过程中,信号沿到达的地方,信号线和参考平面(电源或地平面)间由于电场的建立,会产生一个瞬间电流,如果传输线是各向同性的,那么只要信号在传输,就始终存在一个电流I,而如果信…