红黑树与JAVA实现
Posted 顧棟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了红黑树与JAVA实现相关的知识,希望对你有一定的参考价值。
红黑树与JAVA实现
文章目录
定义
红黑树是一种平衡二叉查找树。红黑树的每个结点上会多出一个存储位表示结点的颜色,颜色只能是红色或黑色。
特性
1. 每个结点的或是黑色或是红色
2. 根结点是黑色
3. 每个叶子结点都是黑色(NIL)
4. 如果一个结点是红色,那么他的子结点必须是黑色
5. 对任意一结点,该结点到其叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点
平衡操作
变色+左旋/右旋
左旋
对A结点进行左旋 就是将A结点的右子结点设为A的父结点,即将A结点变为一个左结点。
右旋
对A结点进行右旋 就是将A结点的左子结点设为A的父结点,即将A结点变为一个右结点。
主要过程
插入过程
向上回溯满足特性
新插入的结点总是设为红色的,所以如果父结点为黑色,就不需要修复,因为没有任何性质被改变,所以只有在父结点为红色结点时需要做修复操作。
父结点是祖父结点的左子结点
情况1:当前结点P的父结点B是红色,且祖父结点的另一个子结点(叔叔结点)C也是红色
对策 :
- 变色:把父结点B和叔叔结点B变黑,祖父A结点涂红
- 向上回溯:然后把当前结点指针给到爷爷,让祖父结点那层继续循环,接受红黑树特性检测。直到根结点,将根结点保持为黑色。
情况2:当前结点P的父结点B是红色,叔叔结点C是黑色,且当前结点是其父结点的左子结点
对策:
- 变色:把父结点B变黑,祖父结点A变红
- 旋转:以祖父结点A为支点右旋
情况3:当前结点P的父结点B是红色,叔叔结点C是黑色,当前结点是父结点的右子结点
对策:
- 旋转:以P的父结点B作为支点左旋
- 当前结点改为P的父结点B
- 此时情况与情况2一致,执行情况2的对策操作。
父结点是祖父结点的右子结点
情况1:父结点与叔叔结点都是红色
对策 :
- 变色:把父结点B和叔叔结点B变黑,祖父A结点涂红
- 向上回溯:然后把当前结点指针给到祖父,让祖父结点那层继续循环,接受红黑树特性检测。直到根结点,将根结点保持为黑色。
情况2:当前结点P的父结点B是红色,叔叔结点C是黑色,且当前结点是其父结点的右子结点
对策 :
- 变色:把父结点B变黑,祖父结点A变红
- 旋转:对祖父结点A进行左旋操作
情况3:当前结点P的父结点B是红色,叔叔结点C是黑色,且当前结点是其父结点的左子结点
对策 :
- 把当前结点指向父结点C
- 旋转:以当前结点C进行右旋
- 此时情况与情况2一致,执行情况2的对策操作。
删除过程
在删除一个结点后,如果删除的结点时红色结点,那么红黑树的性质并不会被影响,此时不需要修正,如果删除的是黑色结点,原红黑树的性质就会被改变,此时我们需要做修正。
当前结点是父结点的左子结点
情况1:兄弟结点为红色
这个时候其实不用管左右侄子是什么颜色的。
对策 :
- 变色:将兄弟变成黑色,父结点变成红色
- 旋转:以父结点为根,进行左旋
- 此时结点情况可能会出现情况2,3,4中的一种
情况2:兄弟为黑色,左右侄子也是黑色
我的理解是,兄弟结点拥有的实际的左右子是NIL,NIL本身就是黑色的。
对策 :
- 变色:兄弟变红
- 当前结点指向父结点,继续判断处理
情况3:兄弟为黑色,右侄子为黑色
对策 :
- 变色:左侄子变为黑色,兄弟变红
- 旋转:以兄弟为支点右旋
- 此时应该是情况4的场景,继续处理
情况4:兄弟为黑色,右侄子为红色
对策 :
- 变色:兄弟颜色变为双亲结点的颜色,右侄子和双亲结点变为黑色。
- 旋转:以双亲结点为支点,左旋
- 跳出循环(将当前结点变为root)
当前结点是父结点的右子结点
情况1:兄弟结点为红色
这个时候其实不用管左右侄子是什么颜色的。
对策 :
- 变色:将兄弟变成黑色,父结点变成红色
- 旋转:以父结点为支点,进行右旋
- 此时结点情况可能会出现情况2,3,4中的一种
情况2:兄弟结点为黑色,左右侄子也是黑色
我的理解是,兄弟结点拥有的实际的左右子是NIL,NIL本身就是黑色的。
对策 :
- 变色:兄弟变红,当前结点指向父结点
- 继续判断处理
情况3:兄弟结点为黑色,左侄子为黑色
对策 :
- 变色:右侄子变为黑色,兄弟变红
- 旋转:以兄弟为支点左旋
- 此时应该是情况4的场景,继续处理
情况4:兄弟结点为黑色,左侄子为红色
对策 :
- 变色:兄弟颜色变为双亲结点的颜色,左侄子和双亲结点变为黑色。
- 旋转:以双亲结点为支点,右旋
- 跳出循环(将当前结点变为root)
存储结构
三叉链表
color | value | left | right | parent |
---|
/**
* RB树的结点类
*/
public static class RBNode<T extends Comparable<T>>
/**
* 颜色
*/
private boolean color;
/**
* 关键字(键值)
*/
private T key;
/**
* 左孩子
*/
private RBNode<T> left;
/**
* 右孩子
*/
private RBNode<T> right;
/**
* 父结点
*/
private RBNode<T> parent;
/**
* 构造函数
*/
public RBNode(T key, boolean color, RBNode<T> parent, RBNode<T> left, RBNode<T> right)
this.key = key;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
public RBNode(T key)
this(key, RED, null, null, null);
boolean isBlack()
return this.color;
boolean isRed()
return !this.color;
JAVA 实现
package tree.redblack;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Donny
* @date 2022/5/17 11:41
*/
public class RBTree<T extends Comparable<T>>
private static final boolean RED = false;
private static final boolean BLACK = true;
/**
* 数中结点的数量
*/
AtomicLong size = new AtomicLong(0);
/**
* 根结点
*/
private RBNode<T> root;
public RBNode<T> getRoot()
return root;
/**
* RB树的结点类
*/
public static class RBNode<T extends Comparable<T>>
/**
* 颜色
*/
private boolean color;
/**
* 关键字(键值)
*/
private T key;
/**
* 左孩子
*/
private RBNode<T> left;
/**
* 右孩子
*/
private RBNode<T> right;
/**
* 父结点
*/
private RBNode<T> parent;
/**
* 构造函数
*/
public RBNode(T key, boolean color, RBNode<T> parent, RBNode<T> left, RBNode<T> right)
this.key = key;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
public RBNode(T key, boolean color)
this(key, color, null, null, null);
public RBNode(T key)
this(key, RED, null, null, null);
public boolean isBlack()
return this.color;
public boolean isRed()
return !this.color;
/**
* 以node为支点左旋
*/
private void leftRotate(RBNode<T> node)
RBNode<T> rightChild = node.right;
if (null == rightChild)
throw new IllegalStateException("right is null");
RBNode<T> parent = node.parent;
node.right = rightChild.left;
if (null != node.right)
node.right.parent = node;
rightChild.left = node;
if (null == parent)
this.root = rightChild;
else if (node.parent.left == node)
parent.left = rightChild;
else
parent.right = rightChild;
node.parent = rightChild;
rightChild.parent = parent;
/**
* 以node为支点右旋
*/
private void rightRotate(RBNode<T> node)
RBNode<T> leftChild = node.left;
if (null == leftChild)
throw new IllegalStateException("leftChild is null");
RBNode<T> parent = node.parent;
node.left = leftChild.right;
if (null != node.left)
node.left.parent = node;
leftChild.right = node;
if (null == parent)
this.root = leftChild;
else if (node.parent.left == node)
parent.left = leftChild;
else
parent.right = leftChild;
node.parent = leftChild;
leftChild.parent = parent;
public T insert(T key)
RBNode<T> rbNode = new RBNode<>(key);
return insert(rbNode);
public T insert(RBNode<T> node)
int cmp;
RBNode<T> y = null;
RBNode<T> x = this.root;
// 将红黑树当作一颗二叉查找树,将结点添加到二叉查找树中
while (x != null)
y = x;
cmp = node.key.compareTo(x.key);
if (cmp == 0)
T v = x.key;
x.key = node.key;
return v;
else if (cmp < 0)
x = x.left;
else
x = x.right;
node.parent = y;
if (y != null)
cmp = node.key.compareTo(y.key);
if (cmp < 0)
y.left = node;
else
y.right = node;
else
this.root = node;
size.incrementAndGet();
// 将它重新修正为一颗红黑树
insertFixUp(node);
return null;
/**
* 红黑树插入修正函数
*/
private void insertFixUp(RBNode<T> node)
RBNode<T> parent;
RBNode<T> grandParent;
// 若“结点没有双亲结点,或双亲结点为黑色”,则新增不需要变动
// 若“结点有双亲结点,且双亲结点为红色”,则需要调整
while (this.root != node && null != (parent = node.parent) && parent.color == RED)
grandParent = parent.parent;
//若“双亲结点”是“祖父结点的左孩子”
if (parent == grandParent.left)
// Case 1条件:叔叔结点是红色
//1、父结点设为黑色
//2、叔叔结点设为黑色
//3、祖父结点设为红色
//4、把祖父结点设置为新结点(当前结点)
RBNode<T> uncle = grandParent.right;
if (null != uncle && uncle.color == RED)
uncle.color = BLACK;
parent.color = BLACK;
grandParent.color = RED;
node = grandParent;
else
if (parent.right == node)
// Case 2条件:叔叔是黑色,且当前结点是右孩子
// 1、以父结点为支点左旋
//2、父结点和当前结点互换位置
//3、把父结点设为当前结点
RBNode<T> tmp;
leftRotate(parent);
tmp = parent;
parent = node;
node = tmp;
// Case 3条件:叔叔是黑色,且当前结点是左孩子。
// 1、父结点设为黑色
// 2、祖父结点设为红色
// 3、以祖父结点为支点右旋
parent.color = BLACK;
grandParent.color = RED;
rightRotate(grandParent);
// 若“双亲结点”是“祖父结点的右孩子”
else
// Case 1条件:叔叔结点是红色
RBNode<T> uncle = grandParent.left;
if (uncle != null && uncle.color == RED)
uncle.color = BLACK;
parent.color = BLACK;
grandParent.color = RED;
node = grandParent;
else
// Case 2条件:叔叔是黑色,且当前结点是左孩子
if (parent.left == node)
RBNode<T> tmp;
rightRotate(parent);
tmp = parent;
parent = node;
node = tmp;
// Case 3条件:叔叔是黑色,且当前结点是右孩子。
parent.color = BLACK;
grandParent.color = RED;
leftRotate(grandParent);
// 将根结点设为黑色
this.root.color = BLACK;
/**
* 树中查找包含value的结点
*/
public RBNode<T> find(T value)
RBNode<T> rbNode = getRoot();
while (rbNode != null)
int cmp = rbNode.key.compareTo(value);
if (cmp < 0)
rbNode = rbNode.right;
else if (cmp > 0)
rbNode = rbNode.left;
else
return rbNode;
return null;
/**
* 查询node的后继结点或者前驱结点
*/
private RBNode<T> getSuccessor(RBNode<T> node)
if (node.right != null)
RBNode<T> rightChild = node.right;
while (rightChild.left != null)
rightChild = rightChild.left;
return rightChild;
RBNode<T> parent = node.parent;
while (parent != null && (node == parent.right))
node = parent;
parent = parent.parent;
return parent;
/**
* 查询node的后继结点或者前驱结点
*/
private RBNode<T> getPredecessor(RBNode<T> node)
if (node.left != null)
RBNode<T> leftChild = node.left;
while (leftChild.left != null)
leftChild = leftChild.left;
以上是关于红黑树与JAVA实现的主要内容,如果未能解决你的问题,请参考以下文章