文章目录

  • 1. 红黑树概念
  • 2. 节点定义
  • 3. 旋转操作
  • 4. 插入操作
  • 5. 删除操作
  • 6. 完整代码

1. 红黑树概念


下图就是一棵红黑树:

为了后续操作中不出现空指针异常,可以加入一个额外的哨兵节点T,T作为所有叶子节点的子节点,作为根节点的父节点,节点T要求是黑色,其他属性不做要求,如下图所示:

2. 节点定义

class Node {int key;Node left, right;Node parent;boolean color;public Node(boolean color) {super();this.color = color;}public Node(int key) {super();this.key = key;}@Overridepublic String toString() {return "Node [key=" + key + ", color=" + color + " p=" + parent.key + "]";}}

3. 旋转操作

左旋操作代码:

public void rotateLeft(Node x) {Node y = x.right;x.right = y.left;// y的左子树作为x的右子树if (y.left != nil) {// y存在左子树y.left.parent = x;// 该左子树的父节点变为x}y.parent = x.parent;// x之前的父节点现在成为y的父节点if (x.parent == nil) {// x是根节点root = y;// y替代x成为根节点} else if (x == x.parent.left) {// x是左子节点x.parent.left = y;// y替代x作为左子节点} else {// x是右子节点x.parent.right = y;// y替代x作为右子节点}y.left = x;// x作为y的左子节点x.parent = y;// y作为x的父节点}

右旋操作代码:

public void rotateRight(Node y) {Node x = y.left;y.left = x.right;if (x.right != null) {// x存在右子树x.right.parent = y;// x的右子树的父节点变为y}x.parent = y.parent;// y之前的父节点现在变为x的父节点if (y == nil) {// y是根节点root = x;// x替代y成为根节点} else if (y == y.parent.left) {// y是左子节点y.parent.left = x;// x代替y作为其父节点的左子节点} else {// y是右子节点y.parent.right = x;// x代替y作为其父节点的右子节点}x.right = y;// y变成x的右子节点y.parent = x;// x变成y的父节点}

4. 插入操作

public void insert(Node z) {Node y = nil;// y指向哨兵节点Node x = root;// x指向根节点while (x != nil) {y = x;// y保存更新前的xif (z.key < x.key) {// z的key较小 然后去x左子树中查找x = x.left;} else {// z的key较大 然后去x右子树中查找x = x.right;}}z.parent = y;// y作为z的父节点if (y == nil) {// 说明目前树为空 插入的是第一个节点root = z;// z成为根节点} else if (z.key < y.key) {y.left = z;// z的值比父节点值小 作为左子节点} else {y.right = z;// z的值比父节点值大 作为右子节点}z.left = nil;z.right = nil;// z是叶子节点z.color = RED;// 叶子节点红色insetFix(z);// 插入之后需要调整树的结构}

插入操作完成了还需要进行树的结构调整,先来看下面几种情况:

情况1: 新插入节点z的叔节点y是红色的

上图中的(a)或(b)违反了性质:一个红节点的两个子节点都是黑色的
解决方案:z的父节点和树节点颜色由红变黑,z的爷爷节点颜色由黑变红

             Node y=z.parent.parent.right;if(y.color==RED) {z.parent.color=BLACK;y.color=BLACK;z.parent.parent.color=RED;z=z.parent.parent;//z指向爷爷节点继续处理树的结构}

情况2: 新插入节点z的叔节点y是黑色的并且z是一个右孩子节点
情况3: 新插入节点z的叔节点y是黑色的并且z是一个左孩子节点
情况2和情况3可以放在一张图中来讨论,因为情况2可以通过左旋操作进入情况3,入下图所示:

         else if(z==z.parent.right) {//情况2z=z.parent;rotateLeft(z);z.parent.color=BLACK;z.parent.parent.color=RED;rotateRight(z.parent.parent);}else if(z==z.parent.right) {//情况3z.parent.color=BLACK;z.parent.parent.color=RED;rotateRight(z.parent.parent);}

上面的三种情况都是基于新插入节点z的父节点是一个左子节点的情形,对于z的父节点是一个右子节点的情况也有三种,与上面三种情况对称,关于调整的完整代码如下:

public void insetFix(Node z) {while (z.parent.color == RED) {if (z.parent == z.parent.parent.left) {// z的父节点是左子节点Node y = z.parent.parent.right;if (y != nil && y.color == RED) {// 情况1z.parent.color = BLACK;y.color = BLACK;z.parent.parent.color = RED;z = z.parent.parent;continue;} else if (z == z.parent.right) {// 情况2z = z.parent;rotateLeft(z);}// 情况3z.parent.color = BLACK;z.parent.parent.color = RED;rotateRight(z.parent.parent);} else if (z.parent == z.parent.parent.right) {// z的父节点是右子节点Node y = z.parent.parent.left;if (y != nil && y.color == RED) {z.parent.color = BLACK;y.color = BLACK;z.parent.parent.color = RED;z = z.parent.parent;continue;} else if (z == z.parent.left) {z = z.parent;rotateRight(z);}z.parent.color = BLACK;z.parent.parent.color = RED;rotateLeft(z.parent.parent);}root.color = BLACK;}}

5. 删除操作

红黑树的删除操作和BST的删除操作类似,如果待删除节点x没有左子节点,就用右子节点替代x;如果待删除节点x没有右子节点,就用左子节点替代x; 如果x既有左子节点也有右子节点,可以使用x右子树中的最小节点y来替代x, y之前的右子树来替换y, x的右子树作为y新的右子树,只不过在红黑树中需要根据节点的颜色进行树结构的调整

// 用节点v 替代 节点upublic void transplant(Node u, Node v) {if (u.parent == T) {// u是根节点T.left = v;// v替代u作为根节点} else if (u == u.parent.left) {// u是左子节点u.parent.left = v;} else if (u == u.parent.right) {u.parent.right = v;}v.parent = u.parent;// u的父节点成为v的父节点}public void delete(Node z) {//z是待删除节点Node y = z;Node x = null;boolean originColor = y.color;if (z.left == T) {// z没有左子节点x = z.right;transplant(z, z.right);//z的右子节点替代z} else if (z.right == T) {//z没有右子节点x = z.left;transplant(z, z.left);//z的左子节点替代z}else {//z的左右子节点都存在y=minimum(z.right);//y保存z右子树中的最小节点 用y替换zoriginColor=y.color;x=y.right;if(y.parent==z) {//y是z的直接右子节点 不需要else语句中的y的右子树替换yx.parent=y;}else {transplant(y, y.right);//y的右子树替换yy.right=z.right;//待删除节点z的右子树成为y的右子树y.right.parent=y;//更新y的右子树的的父节点}transplant(z, y);//用y替换zy.left=z.left;//z的左子树作为y的左子树y.left.parent=y;y.color=z.color;//更新颜色}if(originColor==BLACK) {//待删除节点的颜色是黑色 需要调整树的结构deleteFix(x);}}/** 寻找某个子树中的最小节点*/Node minimum(Node subTreeRoot) {while (subTreeRoot.left != T) {subTreeRoot = subTreeRoot.left;}return subTreeRoot;}

删除-调整操作:

情形1:待调整节点x的兄弟节点w是红色

                 w.color=BLACK;x.parent.color=RED;rotateLeft(x.parent);w=x.parent.right;

情形2:待调整节点x的兄弟节点w是黑色的,并且w的两个子节点也是黑色的

                 w.color=RED;x=x.parent;continue;

情形3:待调整节点x的兄弟节点w是黑色的,w的左子节点为红色,右子节点为黑色

                 w.left.color=BLACK;w.color=RED;rotateRight(w);w=x.parent.right;

情形4:待调整节点x的兄弟节点w是黑色的,w的右子节点为红色,左子节点不考虑

             w.color=x.parent.color;//case4x.parent.color=BLACK;w.right.color=BLACK;rotateLeft(x.parent);x=T.left;

可以发现情况1经过转化可以变成情况2,3,4中的一种,情况3经过转化后就变成了情况4

上面的4种情况是基于x是左子节点的情况,对于x是右子节点的情况,只一将上面代码中的left和right互换即可

刪除-调整代码如下:

public void deleteFix(Node x) {Node w = null;while (x != root && x.color == BLACK) {if (x == x.parent.left) {w = x.parent.right;if (w.color == RED) {// case1w.color = BLACK;x.parent.color = RED;rotateLeft(x.parent);w = x.parent.right;}if (w.left.color == BLACK && w.right.color == BLACK) {// case2w.color = RED;x = x.parent;continue;} else if (w.right.color == BLACK) {// case3w.left.color = BLACK;w.color = RED;rotateRight(w);w = x.parent.right;}w.color = x.parent.color;// case4x.parent.color = BLACK;w.right.color = BLACK;rotateLeft(x.parent);x = root;} else {w = x.parent.left;if (w.color == RED) {// case1w.color = BLACK;x.parent.color = RED;rotateRight(w);w = x.parent.left;}if (w.left.color == BLACK && w.right.color == BLACK) {// case2w.color = RED;x = x.parent;continue;} else if (w.right.color == BLACK) {// case3w.right.color = BLACK;w.color = RED;rotateLeft(w);w = x.parent.left;}w.color = x.parent.color;// case4x.parent.color = BLACK;w.left.color = BLACK;rotateRight(x.parent);x = root;}}x.color = BLACK;}

6. 完整代码

package datastructure.tree;import java.util.LinkedList;
import java.util.Queue;class Node {int key;Node left, right;Node parent;boolean color;public Node(boolean color) {super();this.color = color;}public Node(int key) {super();this.key = key;}@Overridepublic String toString() {return "Node [key=" + key + ", color=" + color + " p=" + parent.key + "]";}}public class RedBlackTree {private static final boolean RED = true;private static final boolean BLACK = false;private Node nil;private Node root;/** 初始化操作中创建一个哨兵节点nil root开始等于nil*/public RedBlackTree() {nil = new Node(BLACK);nil.key = -1;root = nil;}public void rotateLeft(Node x) {Node y = x.right;x.right = y.left;// y的左子树作为x的右子树if (y.left != nil) {// y存在左子树y.left.parent = x;// 该左子树的父节点变为x}y.parent = x.parent;// x之前的父节点现在成为y的父节点if (x.parent == nil) {// x是根节点root = y;// y替代x成为根节点} else if (x == x.parent.left) {// x是左子节点x.parent.left = y;// y替代x作为左子节点} else {// x是右子节点x.parent.right = y;// y替代x作为右子节点}y.left = x;// x作为y的左子节点x.parent = y;// y作为x的父节点}public void rotateRight(Node y) {Node x = y.left;y.left = x.right;if (x.right != null) {// x存在右子树x.right.parent = y;// x的右子树的父节点变为y}x.parent = y.parent;// y之前的父节点现在变为x的父节点if (y == nil) {// y是根节点root = x;// x替代y成为根节点} else if (y == y.parent.left) {// y是左子节点y.parent.left = x;// x代替y作为其父节点的左子节点} else {// y是右子节点y.parent.right = x;// x代替y作为其父节点的右子节点}x.right = y;// y变成x的右子节点y.parent = x;// x变成y的父节点}public void insert(Node z) {Node y = nil;// y指向哨兵节点Node x = root;// x指向根节点while (x != nil) {y = x;// y保存更新前的xif (z.key < x.key) {// z的key较小 然后去x左子树中查找x = x.left;} else {// z的key较大 然后去x右子树中查找x = x.right;}}z.parent = y;// y作为z的父节点if (y == nil) {// 说明目前树为空 插入的是第一个节点root = z;// z成为根节点} else if (z.key < y.key) {y.left = z;// z的值比父节点值小 作为左子节点} else {y.right = z;// z的值比父节点值大 作为右子节点}z.left = nil;z.right = nil;// z是叶子节点z.color = RED;// 叶子节点红色insetFix(z);// 插入之后需要调整树的结构}public void insetFix(Node z) {while (z.parent.color == RED) {if (z.parent == z.parent.parent.left) {// z的父节点是左子节点Node y = z.parent.parent.right;if (y != nil && y.color == RED) {// 情况1z.parent.color = BLACK;y.color = BLACK;z.parent.parent.color = RED;z = z.parent.parent;continue;} else if (z == z.parent.right) {// 情况2z = z.parent;rotateLeft(z);}//情况2经过调整之后可以变成情况3// 情况3z.parent.color = BLACK;z.parent.parent.color = RED;rotateRight(z.parent.parent);} else if (z.parent == z.parent.parent.right) {// z的父节点是右子节点Node y = z.parent.parent.left;if (y != nil && y.color == RED) {z.parent.color = BLACK;y.color = BLACK;z.parent.parent.color = RED;z = z.parent.parent;continue;} else if (z == z.parent.left) {z = z.parent;rotateRight(z);}z.parent.color = BLACK;z.parent.parent.color = RED;rotateLeft(z.parent.parent);}root.color = BLACK;}}// 用节点v 替代 节点upublic void transplant(Node u, Node v) {if (u.parent == nil) {// u是根节点root = v;// v替代u作为根节点} else if (u == u.parent.left) {// u是左子节点u.parent.left = v;} else if (u == u.parent.right) {u.parent.right = v;}v.parent = u.parent;// u的父节点成为v的父节点}public void delete(Node z) {Node y = z;Node x = null;boolean originColor = y.color;if (z.left == nil) {// z没有左子节点x = z.right;transplant(z, z.right);// z的右子节点替代z} else if (z.right == nil) {// z没有右子节点x = z.left;transplant(z, z.left);// z的左子节点替代z} else {// z的左右子节点都存在y = minimum(z.right);// y保存z右子树中的最小节点 用y替换zoriginColor = y.color;x = y.right;if (y.parent == z) {// y是z的直接右子节点 不需要else语句中的y的右子树替换yx.parent = y;} else {transplant(y, y.right);// y的右子树替换yy.right = z.right;// 待删除节点z的右子树成为y的右子树y.right.parent = y;// 更新y的右子树的的父节点}transplant(z, y);// 用y替换zy.left = z.left;y.left.parent = y;y.color = z.color;}if (originColor == BLACK) {// 节点y的颜色是黑色 需要调整树的结构deleteFix(x);}}/** 寻找某个子树中的最小节点*/Node minimum(Node subTreeRoot) {while (subTreeRoot.left != nil) {subTreeRoot = subTreeRoot.left;}return subTreeRoot;}public void deleteFix(Node x) {Node w = null;while (x != root && x.color == BLACK) {if (x == x.parent.left) {w = x.parent.right;if (w.color == RED) {// case1w.color = BLACK;x.parent.color = RED;rotateLeft(x.parent);w = x.parent.right;}if (w.left.color == BLACK && w.right.color == BLACK) {// case2w.color = RED;x = x.parent;continue;} else if (w.right.color == BLACK) {// case3w.left.color = BLACK;w.color = RED;rotateRight(w);w = x.parent.right;}//case3 经过调整之后可以变成case4w.color = x.parent.color;// case4x.parent.color = BLACK;w.right.color = BLACK;rotateLeft(x.parent);x = root;} else {w = x.parent.left;if (w.color == RED) {// case1w.color = BLACK;x.parent.color = RED;rotateRight(w);w = x.parent.left;}if (w.left.color == BLACK && w.right.color == BLACK) {// case2w.color = RED;x = x.parent;continue;} else if (w.right.color == BLACK) {// case3w.right.color = BLACK;w.color = RED;rotateLeft(w);w = x.parent.left;}w.color = x.parent.color;// case4x.parent.color = BLACK;w.left.color = BLACK;rotateRight(x.parent);x = root;}}x.color = BLACK;}/** 层次遍历*/public void printTreeByLevel(Node root) {Queue<Node> q = new LinkedList<>();q.offer(root);while (!q.isEmpty()) {int sz = q.size();Node node = null;for (int i = 0; i < sz; i++) {node = q.poll();System.out.print(node + " ");if (node.left != nil)q.offer(node.left);if (node.right != nil)q.offer(node.right);}System.out.println();}}/** 根据key查找某个节点*/public Node find(Node root, int key) {if (root == nil) {return null;}if (root.key < key) {return find(root.right, key);} else if (root.key > key) {return find(root.left, key);} else if (key == root.key) {return root;}return null;}/** 中序遍历*/public void printInorder(Node root) {if (root == nil)return;printInorder(root.left);System.out.println(root);printInorder(root.right);}public static void main(String[] args) {RedBlackTree tree = new RedBlackTree();System.out.println("插入数据:");for (int i = 1; i <= 10; i++) {tree.insert(new Node(i));}System.out.println("层次遍历:");tree.printTreeByLevel(tree.root);System.out.println("中序遍历:");tree.printInorder(tree.root);System.out.println("删除数据:");int[] delete = { 1, 2,3, 4,5, 6,7,8, 9,10 };for (int num : delete) {Node delNode = tree.find(tree.root, num);System.out.println("删除了元素" + delNode);tree.delete(delNode);System.out.println("中序遍历:");tree.printInorder(tree.root);}}
}

红黑树原理详解及代码实现相关推荐

  1. 红黑树原理详解及golang实现

    红黑树原理详解及golang实现 文章目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 `情形1`:空树 `情形2`:插入节点父节为黑色, `情 ...

  2. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

  3. STL源码剖析---红黑树原理详解上

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7740956 一.红黑树概述 红黑树和我们以前学过的AVL树类似,都是在进 ...

  4. STL源码剖析---红黑树原理详解

    红黑树概述 红黑树都是在进行插入和删除时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.红黑树追求的时局部平衡而不是AVL树中的非常严格的平衡. 所谓红黑树,不仅是一个二叉搜索树,而且必须满 ...

  5. 图像质量损失函数SSIM Loss的原理详解和代码具体实现

    本文转自微信公众号SIGAI 文章PDF见: http://www.tensorinfinity.com/paper_164.html http://www.360doc.com/content/19 ...

  6. TOPSIS(逼近理想解)算法原理详解与代码实现

    写在前面: 个人理解:针对存在多项指标,多个方案的方案评价分析方法,也就是根据已存在的一份数据,判断数据中各个方案的优劣.中心思想是首先确定各项指标的最优理想值(正理想值)和最劣理想值(负理想解),所 ...

  7. 红黑树操作详解——很形象的过程

    红黑树是一种很好的自平衡二叉排序树,在此,给出一个网友给出的红黑树操作详解: https://segmentfault.com/a/1190000012728513 里面给出了红黑树的详细操作,过程很 ...

  8. 冒泡排序原理详解及代码实现

    1.冒泡排序数组排序常用的一种方式,为什么要叫冒泡排序呢?这还要从它的原理说起. 2.代码实现(低效版) 3.原理详解:冒泡排序最基本的思想就是从左到右依次判断相邻的两个数的大小关系,如果前面的数大于 ...

  9. Huffman 编码原理详解(代码示例)

    1.概述 huffman编码是一种可变长编码(  VLC:variable length coding))方式,于1952年由huffman提出.依据字符在需要编码文件中出现的概率提供对字符的唯一编码 ...

最新文章

  1. WPF 4 Ribbon 开发 之 快捷工具栏(Quick Access Toolbar)
  2. Python基础08 面向对象的基本概念
  3. java包含点_Java的21个核心技术点,你知道吗
  4. Eclipse Memory Analyzer 安装(Update Site: http://download.eclipse.org/mat/1.3.1/update-site/ )
  5. bootstrap学习(五)代码
  6. java8的路径_什么是路径?
  7. session会话拦截ajax,session过期,拦截ajax请求并跳转登录页面
  8. BE的完整形式是什么?
  9. oracle应收模块核销点不上,详解EBS接口开发之应收款处理
  10. Python | threading03 - 使用条件对象,实现线程间的同步
  11. 服务器数据库密码修改了,服务器密码修改后数据库
  12. VB2010实例(2) _滚动字幕
  13. 格力空调通讯协议_格力空调485通讯协议格式 485总线上modbus通信协议?
  14. 《大数据之路-阿里巴巴大数据实践》第十六章 数据应用
  15. div布局三栏-左中右
  16. 飞思卡尔系列单片机的censorship的使用
  17. NYOJ_1248_海岛争霸【最短路】
  18. 四、AOSP-开机报错
  19. 2021-09-21KNN——鸢尾花
  20. 夜深模拟器调试下载的app

热门文章

  1. JS创建数组的三种方法
  2. excel 插件禁用
  3. 小米5 流量显示无服务器,出乎意料!小米Play这配置,简直伤透米粉心!自带流量能否挽救?...
  4. 可以视频翻译的软件有哪些?1分钟告诉你三个视频翻译软件推荐
  5. linux环境下python读取pdf转docx,docx转txt,txt转excel
  6. 安装下载如何让youku 没有广告!!
  7. 点击按钮显示或隐藏Div块
  8. Xmind思维导图转化为Excel用例
  9. iOS---集成融云即时通讯详细教程
  10. v-echarts的介绍及使用