java实现红黑树

红黑树

红黑树是一种自平衡二叉查找树,其中每个节点都有一个颜色属性,颜色为红色或黑色。它的特性保证了树在插入和删除操作后仍然保持大致的平衡,使得查找操作能够在对数时间内完成。以下是红黑树的一些基本性质:

  1. 每个节点是红色或黑色。
  2. 根节点是黑色。
  3. 所有叶子(NIL节点)都是黑色。
  4. 每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

请添加图片描述

以下是红黑树的核心代码解析:

public class RedBlackTree {enum Color {RED, BLACK;}Node root;static class Node {int key;Object value;Node left;Node right;Node parent;        // 父节点Color color = RED;  // 默认插入的节点为红色// 构造函数和辅助函数}

构建节点和辅助函数

static class Node {// 构造函数,用于创建新节点public Node(int key, Object value) {this.key = key;this.value = value;}// 判断当前节点是否为左孩子boolean isLeftChild() {return parent != null && parent.left == this;}// 叔叔Node uncle() {if (parent == null || parent.parent == null) {return null;}if (parent.isLeftChild()) {return parent.parent.right;} else {return parent.parent.left;}}// 兄弟Node sibling() {if (parent == null) {return null;}if (this.isLeftChild()) {return parent.right;} else {return parent.left;}}
}

旋转操作(leftRotate & rightRotate)

为了维持红黑树的平衡性,在插入和删除节点时可能会用到旋转操作。

private void rightRotate(Node pink) {Node parent = pink.parent;Node yellow = pink.left;Node green = yellow.right;if (green != null) {green.parent = pink;}yellow.right = pink;yellow.parent = parent;pink.left = green;pink.parent = yellow;if (parent == null) {root = yellow;} else if (parent.left == pink) {parent.left = yellow;} else {parent.right = yellow;}
}// 左旋
private void leftRotate(Node pink) {Node parent = pink.parent;Node yellow = pink.right;Node green = yellow.left;if (green != null) {green.parent = pink;}yellow.left = pink;yellow.parent = parent;pink.right = green;pink.parent = yellow;if (parent == null) {root = yellow;} else if (parent.left == pink) {parent.left = yellow;} else {parent.right = yellow;}
}

旋转操作的目的是重新分布节点,使得树的高度保持平衡,同时需要更新父节点和子节点的引用。

插入(put)

当向红黑树中插入新节点时,需要保证树的性质不被破坏。插入操作后,可能需要通过一系列的旋转和着色来修正树,保持红黑树的性质。

public void put(int key, Object value) {Node p = root;Node parent = null;while (p != null) {parent = p;if (key < p.key) {p = p.left;} else if (p.key < key) {p = p.right;} else {p.value = value; // 更新return;}}Node inserted = new Node(key, value);if (parent == null) {root = inserted;} else if (key < parent.key) {parent.left = inserted;inserted.parent = parent;} else {parent.right = inserted;inserted.parent = parent;}fixRedRed(inserted);
}void fixRedRed(Node x) {// case 1 插入节点是根节点,变黑即可if (x == root) {x.color = BLACK;return;}// case 2 插入节点父亲是黑色,无需调整if (isBlack(x.parent)) {return;}/* case 3 当红红相邻,叔叔为红时需要将父亲、叔叔变黑、祖父变红,然后对祖父做递归处理*/Node parent = x.parent;Node uncle = x.uncle();Node grandparent = parent.parent;if (isRed(uncle)) {parent.color = BLACK;uncle.color = BLACK;grandparent.color = RED;fixRedRed(grandparent);return;}// case 4 当红红相邻,叔叔为黑时if (parent.isLeftChild() && x.isLeftChild()) { // LLparent.color = BLACK;grandparent.color = RED;rightRotate(grandparent);} else if (parent.isLeftChild()) { // LRleftRotate(parent);x.color = BLACK;grandparent.color = RED;rightRotate(grandparent);} else if (!x.isLeftChild()) { // RRparent.color = BLACK;grandparent.color = RED;leftRotate(grandparent);} else { // RLrightRotate(parent);x.color = BLACK;grandparent.color = RED;leftRotate(grandparent);}
}

删除(remove)

删除操作是红黑树中比较复杂的部分。在删除节点时,同样需要通过旋转和重新着色操作来保持红黑树的性质。

public void remove(int key) {Node deleted = find(key);if (deleted == null) {return;}doRemove(deleted);
}public boolean contains(int key) {return find(key) != null;
}// 查找删除节点
private Node find(int key) {Node p = root;while (p != null) {if (key < p.key) {p = p.left;} else if (p.key < key) {p = p.right;} else {return p;}}return null;
}// 查找剩余节点
private Node findReplaced(Node deleted) {if (deleted.left == null && deleted.right == null) {return null;}if (deleted.left == null) {return deleted.right;}if (deleted.right == null) {return deleted.left;}Node s = deleted.right;while (s.left != null) {s = s.left;}return s;
}// 处理双黑 (case3、case4、case5)
private void fixDoubleBlack(Node x) {if (x == root) {return;}Node parent = x.parent;Node sibling = x.sibling();// case 3 兄弟节点是红色if (isRed(sibling)) {if (x.isLeftChild()) {leftRotate(parent);} else {rightRotate(parent);}parent.color = RED;sibling.color = BLACK;fixDoubleBlack(x);return;}if (sibling != null) {// case 4 兄弟是黑色, 两个侄子也是黑色if (isBlack(sibling.left) && isBlack(sibling.right)) {sibling.color = RED;if (isRed(parent)) {parent.color = BLACK;} else {fixDoubleBlack(parent);}}// case 5 兄弟是黑色, 侄子有红色else {// LLif (sibling.isLeftChild() && isRed(sibling.left)) {rightRotate(parent);sibling.left.color = BLACK;sibling.color = parent.color;}// LRelse if (sibling.isLeftChild() && isRed(sibling.right)) {sibling.right.color = parent.color;leftRotate(sibling);rightRotate(parent);}// RLelse if (!sibling.isLeftChild() && isRed(sibling.left)) {sibling.left.color = parent.color;rightRotate(sibling);leftRotate(parent);}// RRelse {leftRotate(parent);sibling.right.color = BLACK;sibling.color = parent.color;}parent.color = BLACK;}} else {// @TODO 实际也不会出现,触发双黑后,兄弟节点不会为 nullfixDoubleBlack(parent);}
}private void doRemove(Node deleted) {Node replaced = findReplaced(deleted);Node parent = deleted.parent;// 没有孩子if (replaced == null) {// case 1 删除的是根节点if (deleted == root) {root = null;} else {if (isBlack(deleted)) {// 复杂调整fixDoubleBlack(deleted);} else {// 红色叶子, 无需任何处理}if (deleted.isLeftChild()) {parent.left = null;} else {parent.right = null;}deleted.parent = null;}return;}// 有一个孩子if (deleted.left == null || deleted.right == null) {//  删除的是根节点if (deleted == root) {root.key = replaced.key;root.value = replaced.value;root.left = root.right = null;} else { // 只可能是红节点if (deleted.isLeftChild()) {parent.left = replaced;} else {parent.right = replaced;}replaced.parent = parent;deleted.left = deleted.right = deleted.parent = null;replaced.color = BLACK;}return;}// case 0 有两个孩子 => 有一个孩子 或 没有孩子int t = deleted.key;deleted.key = replaced.key;replaced.key = t;Object v = deleted.value;deleted.value = replaced.value;replaced.value = v;doRemove(replaced);
}

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

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

相关文章

JS 数组对象的 34 种官方用法

数组&#xff08;Array&#xff09;作为 JavaScript 位列第一的对象&#xff0c; 其重要性可见一般&#xff0c;在这里就让我们来详细的扒一扒数组对象都有哪些方法&#xff0c;它们分别能对数组做什么。 一、数组是什么 官方对于 Array&#xff08;数组&#xff09;对象的解释…

面试题16.15.珠玑妙算

前言 这两天突然发现力扣上还是有我能写出来的题的&#xff0c;虽说都是简单级别的&#xff08;以及一道中等的题&#xff09;&#xff0c;但是能写出来力扣真的太开心了&#xff0c;&#xff08;大佬把我这段话当个玩笑就行了&#xff09;&#xff0c;于是乎&#xff0c;我觉…

C#,入门教程(21)——命名空间(namespace)与程序结构的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(20)——列表&#xff08;List&#xff09;的基础知识https://blog.csdn.net/beijinghorn/article/details/124094382 编写软件&#xff08;大软件称为系统&#xff09;与盖大楼一个道理。 假设咱们现在需要盖一座名为“天梯大厦”的…

kubernetes工作负载-DamonSet

一、DemonSet的介绍 1、什么是DemonSet DaemonSet 控制器是用来保证在所有节点上运行一个 Pod 的副本当有节点加入集群时&#xff0c; 也会为他们新增一个 Pod。 当有节点从集群移除时&#xff0c;这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 简而言之…

进入docker容器,vi: command not found

问题描述&#xff1a; 进入docker容器&#xff0c;查看文件执行vim 命令&#xff0c;报错 vim: command not found。搜索解决方案&#xff0c;说执行一下 apt-get install vim命令&#xff0c;然后又报错 Unable to locate package vim。 解决&#xff1a; 1.执行 npt-get up…

YOLOv3:算法与论文详细解读

【yolov1&#xff1a;背景介绍与算法精讲】 【yolo9000&#xff1a;Better, Faster, Stronger的目标检测网络】 目录 一、YOLOv3概述二、创新与改进三、改进细节3.1 多尺度特征3.2 不同尺度先验框3.3 完整的网络结构3.3 Darknet-53主干网络3.4 残差网络3.4.1 恒等映射3.4.2 网络…

git提交代码到远端仓库的方法详解

一、何为git git就是版本控制器&#xff0c;就比如说你新建了一个git文件夹&#xff0c;里面用于存放你的C语言实习报告&#xff0c;现在要用git对该文件夹进行接管。当你修改了你的C语言实习报告点击保存之后&#xff0c;就用git的相关命令&#xff0c;提交给git&#xff0c;让…

go语言(十)---- 面向对象封装

面向对象的封装 package mainimport "fmt"type Hero struct {Name stringAd intLevel int }func (this Hero) Show(){fmt.Println("Name ", this.Name)fmt.Println("Ad ", this.Ad)fmt.Println("Level ", this.Level)}func (thi…

priority_queue的使用与模拟实现(容器适配器+stack与queue的模拟实现源码)

priority_queue的使用与模拟实现 引言&#xff08;容器适配器&#xff09;priority_queue的介绍与使用priority_queue介绍接口使用默认成员函数 size与emptytoppush与pop priority_queue的模拟实现构造函数size与emptytoppush与pop向上调整建堆与向下调整建堆向上调整建堆向下调…

个人实现的QT拼图游戏(开源),QT拖拽事件详解

文章目录 效果图引言玩法 拖拽概念基本概念如何在Qt中使用拖放注意事项 游戏关键问题总结 效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c6dd66befd314442adf07e1dec0d550c.png 引言 在学习QT demo时&#xff0c;发现有一个拼图demo&#xff0c;介绍拖…

RHCE: 主从DNS服务器配置 (实现正反向解析)

主服务器配置: 准备工作: #关闭防火墙 [root192 ~]# systemctl stop firewalld#关闭selinux [root192 ~]# setenforce 0#查看selinux状态 [root192 ~]# getenforce Permissive#安装bind包 [root192 ~]# yum install bind -y#查询软件包下的文件 /etc/named.conf #主配置文…

美易官方:美股芯片股盘前走高

随着全球经济的逐步复苏&#xff0c;特别是科技行业的快速发展&#xff0c;芯片股作为科技板块的重要组成部分&#xff0c;在美股市场的表现尤为引人注目。近期&#xff0c;美股芯片股在盘前交易中持续走高&#xff0c;其中AMD的涨幅超过2%&#xff0c;ARM和英伟达也分别涨超1%…

【硬件安全】硬件安全模块—HSM

Perface 硬件安全模块&#xff08;英语&#xff1a;Hardware security module&#xff0c;缩写HSM&#xff09;是一种用于保障和管理强认证系统所使用的数字密钥&#xff0c;并同时提供相关密码学操作的计算机硬件设备。 硬件安全模块一般通过扩展卡或外部设备的形式直接连接…

《设计模式的艺术》笔记 - 职责链模式

介绍 职责链模式避免将请求发送者与接收者耦合在一起&#xff0c;让多个对象都有机会接收请求&#xff0c;将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理它为止。职责链模式是一种对象行为型模式。 实现 myclass.h // // Created by …

【算法Hot100系列】跳跃游戏

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

windows系统中,通过LOAD到入csv格式的文件到neo4j中,如何写文件路径

在Neo4j中&#xff0c;使用LOAD CSV语句导入CSV文件时&#xff0c;需要确保你的文件路径是正确的。如果你使用的是Neo4j Desktop或者Neo4j Server&#xff0c;通常需要将CSV文件放在特定的导入目录下。 例如&#xff0c;如果你使用的是Neo4j Desktop&#xff0c;通常会有一个默…

Linux搭建dns主从服务器

一、实验要求 配置Dns主从服务器&#xff0c;能够实现正常的正反向解析 二、知识点 1、DNS简介 DNS&#xff08;Domain Name System&#xff09;是互联网上的一项服务&#xff0c;它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网。…

js的节流和防抖

在JavaScript中&#xff0c;节流&#xff08;Throttling&#xff09;和防抖&#xff08;Debouncing&#xff09;是两种常用的优化高频率触发的事件或函数调用的技术。 防抖&#xff08;Debouncing&#xff09;&#xff1a; 防抖的基本思想是&#xff1a;一定时间内&#xff0…

【DP】LCR 100.三角形最小路径和

题目 法1&#xff1a;DP class Solution {public int minimumTotal(List<List<Integer>> triangle) {int n triangle.size();if (n 1) {return triangle.get(0).get(0);}int[] tmp new int[n];tmp[0] triangle.get(0).get(0);int ans Integer.MAX_VALUE;for…

磁的基本知识

磁的基本知识。 一、磁铁及其基本性质。 1、磁铁的概念。 具有吸引铁、钴、镍等金属能力的物质叫做磁体&#xff0c;俗称磁铁、吸铁石。被吸引的铁、钴、镍等物质叫做铁磁性材料。磁铁吸引铁磁性材料的性质叫做磁性。 2、磁铁的分类。 磁铁可分为天然磁铁和人造磁铁两种。天然…