文章目录

  • 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. 分类算法之贝叶斯网络(Bayesian networks)
  2. Acdream Path 动态规划
  3. go语言切片切片与指针
  4. 网易云信AI音频最新研究成果获世界顶级学术会议 ICASSP 2022 认可
  5. cytoscape操作经验
  6. 关于Decision in process状态时间变化的解释
  7. 【 理想的机器学习书】
  8. 2021年通达信指标公式大全,值得收藏!
  9. kali暴力破解WiFi
  10. VsCode设置默认浏览器打开
  11. 我的理想600字作文计算机方面,我的理想作文600字当一名老师(共8篇)
  12. 从Bugreport 解读 Android电量统计原理
  13. 强调实体融合的当下,元宇宙当仁不让地成为各色玩家关注的焦点
  14. 大数据周会-本周学习内容总结014
  15. 基于R语言的seasonal包使用手册_06.final(seas(data, na.action = xxxx))
  16. 我想问在刚学c语言时总是头痛 有好方法的吗
  17. 使用DiskGenius进行分区4K扇区对齐检测
  18. 【数据结构与算法基础】并查集原理、封装实现及例题解析(C和java)
  19. Encode Decode
  20. HDU 1253 - 胜利大逃亡

热门文章

  1. Python 爬取“工商秘密”微博后,我做了这个“可视化大屏”(附gif图)
  2. A股2017十大TOP10,大数据榜单透视A股赚钱基因!
  3. 量化学习——跟随龙虎榜交易
  4. HTML5--地理定位与第三方工具百度地图的应用
  5. 做好seo真很需要耐性
  6. 设计模式 - Reactor 模式
  7. 如何将阿里云天气接口中返回的天气图标转换成对应的url
  8. Udacity数据分析(入门)-分析 A/B 测试结果
  9. 苹果漏洞 Siri会泄露你的个人资料
  10. 注册页面 register