查找: 查找的值比根节点大,搜索右子树,比根节点小,搜索左子树
插入: 要插入节点,必须先找到插入节点位置。依然是从根节点开始比较,小于根节点的话就和左子树
比较,反之与右子树比较,直到左子树为空或者右子树为空,则插入到相应为空的位置。
遍历(红黑树通用): 根据一定顺序来访问整个树,常见的有前序遍历,中序遍历(用的较多),后序
遍历
前序遍历:左子树-》根节点-》右子树 123 4 567
中序遍历:根节点-》左子树-》右子树 4 213 657
后续遍历:左子树-》右子树-》根节点 132 576 4
**查找最小值(红黑树通用):**沿着根节点的左子树一路查找,直到最后一个不为空的节点,该节点就是
当前这个树的最小节点
查找最大值(红黑树通用): 沿着根节点的右子树一路查找,直到最后一个不为空的节点,该节点就是
当前这个树的最大节点
查找前驱节点(红黑树通用)(红黑树重点): 小于当前节点的最大值
查找后继节点(红黑树通用)(红黑树重点): 大于当前节点的最小值
2-3-4树是四阶的 B树(Balance Tree),他属于一种多路查找树,它的结构有以下限制:
红色节点的意思就是说,红色是跟父节点(黑色),在2-3-4树的表示下,其实是同一个节点
如图上的值为1的节点和值为2的节点在2-3-4树中就是一个节点有两个值,其他同理
所以为什么红黑树的从任何一个节点到叶子节点,经过的黑色节点是一样的?或者说为什么不能有两个连续的红色节点?
因为黑色才算是真正的节点,红色只是父节点的附庸,红色节点不能附庸在其他红色的节点上面!
插入是比较简单的,比较新增的节点与root的值,比root大往右,比root小往左,找到叶子节点,插入;
红黑树上的新增和删除节点一定是2-3-4树上的叶子节点
看下图,x就是插入的节点,红色箭头的指向是调整之后的局部颜色!
这个图举例了x的父亲节点是爷爷的左孩子的情况下!
x是新插入的节点!
详见fixAfterPut方法
如果x的父亲节点是爷爷的右孩子,情况相反!
什么情况下需要左旋?
当node的右节点是红色并且左节点是黑色(见上图红黑树旋转的情况L)
private void leftRotate(RBNode p) {
if (p != null) {
RBNode r = p.right;
p.right = r.left;
if (r.left != null) {
r.left.parent = p;
}
r.parent = p.parent;
if (p.parent == null) {
root = r;
} else if (p.parent.left == p) {
p.parent.left = r;
} else {
p.parent.right = r;
}
r.left = p;
p.parent = r;
}
}
什么情况下需要右旋?
当node的左节点是红色并且左节点的左节点是红色(见上图红黑树旋转的情况R)
private void rightRotate(RBNode p) {
if (p != null) {
RBNode l = p.left;
p.left = l.right;
if (l.right != null) {
l.right.parent = p;
}
l.parent = p.parent;
if (p.parent == null) {
root = l;
} else if (p.parent.right == p) {
p.parent.right = l;
} else {
p.parent.left = l;
}
l.right = p;
p.parent = l;
}
}
public void put(K key, V value) {
RBNode t = this.root;
//如果是根节点
if (t == null) {
root = new RBNode<>(key, value == null ? key : value, null);
return;
}
int cmp;
//寻找插入位置
//定义一个双亲指针
RBNode parent;
if (key == null) {
throw new NullPointerException();
}
//沿着跟节点寻找插入位置
do {
parent = t;
cmp = key.compareTo((K) t.key);
if (cmp < 0) {
t = t.left;
} else if (cmp > 0) {
t = t.right;
} else {
t.setValue(value == null ? key : value);
return;
}
} while (t != null);
RBNode e = new RBNode<>(key, value == null ? key : value, parent);
//如果比较最终落在左子树,则直接将父节点左指针指向e
if (cmp < 0) {
parent.left = e;
}
//如果比较最终落在右子树,则直接将父节点右指针指向e
else {
parent.right = e;
}
//调整
fixAfterPut(e);
}
private void fixAfterPut(RBNode x) {
x.color = RED;
//本质上就是父节点是黑色就不需要调整,对应情况就是2,3
while (x != null && x != root && x.parent.color == RED) {
//1、x的父节点是爷爷的左孩子(左3)
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//叔叔节点
RBNode y = rightOf(parentOf(parentOf(x)));
//第3种情况
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//爷爷节点递归
x=parentOf(parentOf(x));
}
//第2种情况
else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
leftRotate(x);
}
//父亲变黑
setColor(parentOf(x), BLACK);
//爷爷变红
setColor(parentOf(parentOf(x)), RED);
//根据爷爷节点右旋转
rightRotate(parentOf(parentOf(x)));
}
}
//2、跟第一种情况相反操作
else {
//右3
//叔叔节点
RBNode y = leftOf(parentOf(parentOf(x)));
//第3种情况
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//爷爷节点递归
x=parentOf(parentOf(x));
}
//第2种情况
else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rightRotate(x);
}
//父亲变黑
setColor(parentOf(x), BLACK);
//爷爷变红
setColor(parentOf(parentOf(x)), RED);
//根据爷爷节点右旋转
leftRotate(parentOf(parentOf(x)));
}
}
}
root.color=BLACK;
}
首先,红黑树删除的节点一定是对应2-3-4树的叶子节点,根据前驱或者后继算法查找出来的节点一定是2-3-4树的叶子节点
/**
* 找到指定节点的前驱节点,即找小于node节点的最大值
* @param node
*/
private Node predecessor(Node node){
if(node==null){
return null;
}
else if(node.left!=null){
Node p=node.left;
while(p.right!=null){
p=p.right;
}
return p;
}else{
Node p = node.parent;
Node ch=node;
while(p!=null&&ch==p.left){
ch=p;
p=p.parent;
}
return p;
}
}
/**
* 找后继节点,即大于节点的最小值
* @param node
* @return
*/
private Node successor(Node node){
if(node==null){
return null;
}
else if(node.right!=null){
Node p=node.right;
while(p.left!=null){
p=p.left;
}
return p;
}else{
Node p = node.parent;
Node ch=node;
while(p!=null&&ch==p.right){
ch=p;
p=p.parent;
}
return p;
}
}
public V remove(K key){
RBNode node=getNode(key);
if(node==null){
return null;
}
V oldValue = (V) node.value;
deleteNode(node);
return oldValue;
}
/**
* 删除操作:
* 1、删除叶子节点,直接删除
* 2、删除的节点有一个子节点,那么用子节点来替代
* 3、如果删除的节点有2个子节点,此时需要找到前驱节点或者后继节点来替代
*
* @param node
*/
private void deleteNode(RBNode node){
//3、node节点有2个孩子
if(node.left!=null&&node.right!=null){
/**
* 这里要注意,如果使用下面这个网站演示的话,此网站用的是前驱节点替代
* 下面代码里我使用的是后继节点替代,删除节点后显示可能会和该网站不一致,
* 但是这两种方法红黑树删除都是合法的
* (可以自行把前驱节点替代方案屏蔽放开,后继节点替代方案注释掉测试下)
*
* https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
*/
//后继节点替代
RBNode rep = successor(node);
//前驱节点替代
// RBNode rep= predecessor(node);
node.key = rep.key;
node.value=rep.value;
node=rep;
}
RBNode replacement=node.left!=null?node.left:node.right;
//2、替代节点不为空
if(replacement!=null){
//替代者的父指针指向的原来node的父亲
replacement.parent=node.parent;
//node是根节点
if(node.parent==null){
root=replacement;
}
//node是左孩子,所以替代者依然是左孩子
else if(node==node.parent.left){
node.parent.left=replacement;
}
//node是右孩子,所以替代者依然是右孩子
else{
node.parent.right=replacement;
}
//将node的左右孩子指针和父指针都指向null(此时node处于游离状态,等待垃圾回收)
node.left=node.right=node.parent=null;
//替换完之后需要调整平衡
if(node.color==BLACK){
//需要调整,这种情况一定是红色(替代节点一定是红色,此时只要变色)
fixAfterRemove(replacement);
}
}
//删除节点就是根节点
else if(node.parent==null){
root=null;
}
//1、node节点是叶子节点,replacement为null
else{
//先调整
if(node.color==BLACK) {
fixAfterRemove(node);
}
//再删除
if(node.parent!=null){
if(node==node.parent.left){
node.parent.left=null;
}
else if(node==node.parent.right){
node.parent.right=null;
}
node.parent=null;
}
}
}
private void fixAfterRemove(RBNode x){
while(x!=root&&colorOf(x)==BLACK){
//x是左孩子的情况
if(x==leftOf(parentOf(x))) {
//兄弟节点
RBNode rnode = rightOf(parentOf(x));
//判断此时兄弟节点是否是真正的兄弟节点
if(colorOf(rnode)==RED){
setColor(rnode,BLACK);
setColor(parentOf(x),RED);
leftRotate(parentOf(x));
//找到真正的兄弟节点
rnode=rightOf(parentOf(x));
}
//情况三,找兄弟借,兄弟没得借
if(colorOf(leftOf(rnode))==BLACK&&colorOf(rightOf(rnode))==BLACK){
//情况复杂,暂时不写
setColor(rnode,RED);
x=parentOf(x);
}
//情况二,找兄弟借,兄弟有的借
else{
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if(colorOf(rightOf(rnode))==BLACK){//这里已经是判断出来,一定有一个节点,那么右孩子为空,左孩子一定不为空
setColor(leftOf(rnode),BLACK);
setColor(rnode,RED);
rightRotate(rnode);
rnode=rightOf(parentOf(x));
}
setColor(rnode,colorOf(parentOf(x)));
setColor(parentOf(x),BLACK);
setColor(rightOf(rnode),BLACK);
leftRotate(parentOf(x));
x=root;
}
}
//x是右孩子的情况
else{
//兄弟节点
RBNode rnode = leftOf(parentOf(x));
//判断此时兄弟节点是否是真正的兄弟节点
if(colorOf(rnode)==RED){
setColor(rnode,BLACK);
setColor(parentOf(x),RED);
rightRotate(parentOf(x));
//找到真正的兄弟节点
rnode=leftOf(parentOf(x));
}
//情况三,找兄弟借,兄弟没得借
if(colorOf(rightOf(rnode))==BLACK&&colorOf(leftOf(rnode))==BLACK){
//情况复杂,暂时不写
setColor(rnode,RED);
x=parentOf(x);
}
//情况二,找兄弟借,兄弟有的借
else{
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if(colorOf(leftOf(rnode))==BLACK){
setColor(rightOf(rnode),BLACK);
setColor(rnode,RED);
leftRotate(rnode);
rnode=leftOf(parentOf(x));
}
setColor(rnode,colorOf(parentOf(x)));
setColor(parentOf(x),BLACK);
setColor(leftOf(rnode),BLACK);
rightRotate(parentOf(x));
x=root;
}
}
}
//情况一、替代节点是红色,则直接染红,补偿删除的黑色节点,这样红黑树依然保持平衡
setColor(x,BLACK);
}
public class RBTree, V> {
private static final boolean RED = false;
private static final boolean BLACK = true;
private RBNode root;
public RBNode getRoot() {
return root;
}
public void setRoot(RBNode root) {
this.root = root;
}
private void leftRotate(RBNode p) {
if (p != null) {
RBNode r = p.right;
p.right = r.left;
if (r.left != null) {
r.left.parent = p;
}
r.parent = p.parent;
if (p.parent == null) {
root = r;
} else if (p.parent.left == p) {
p.parent.left = r;
} else {
p.parent.right = r;
}
r.left = p;
p.parent = r;
}
}
/**
* 右旋
* pf pf
* \ \
* p (l)pl
* / \ => / \
*(l)pl pr ll p
* / \ / \
* ll lr lr pr
*
* @param p
*/
private void rightRotate(RBNode p) {
if (p != null) {
RBNode l = p.left;
p.left = l.right;
if (l.right != null) {
l.right.parent = p;
}
l.parent = p.parent;
if (p.parent == null) {
root = l;
} else if (p.parent.right == p) {
p.parent.right = l;
} else {
p.parent.left = l;
}
l.right = p;
p.parent = l;
}
}
/**
* 找到指定节点的前驱节点,即找小于node节点的最大值
* @param node
*/
private RBNode predecessor(RBNode node){
if(node==null){
return null;
}
else if(node.left!=null){
RBNode p=node.left;
while(p.right!=null){
p=p.right;
}
return p;
}else{
RBNode p = node.parent;
RBNode ch=node;
while(p!=null&&ch==p.left){
ch=p;
p=p.parent;
}
return p;
}
}
/**
* 找后继节点,即大于节点的最小值
* @param node
* @return
*/
private RBNode successor(RBNode node){
if(node==null){
return null;
}
else if(node.right!=null){
RBNode p=node.right;
while(p.left!=null){
p=p.left;
}
return p;
}else{
RBNode p = node.parent;
RBNode ch=node;
while(p!=null&&ch==p.right){
ch=p;
p=p.parent;
}
return p;
}
}
private boolean colorOf(RBNode node) {
return node == null ? BLACK : node.color;
}
private RBNode parentOf(RBNode node) {
return node != null ? node.parent : null;
}
private RBNode leftOf(RBNode node) {
return node != null ? node.left : null;
}
private RBNode rightOf(RBNode node) {
return node != null ? node.right : null;
}
private void setColor(RBNode node, boolean color){
if(node!=null){
node.color=color;
}
}
public void put(K key, V value) {
RBNode t = this.root;
//如果是根节点
if (t == null) {
root = new RBNode<>(key, value == null ? key : value, null);
return;
}
int cmp;
//寻找插入位置
//定义一个双亲指针
RBNode parent;
if (key == null) {
throw new NullPointerException();
}
//沿着跟节点寻找插入位置
do {
parent = t;
cmp = key.compareTo((K) t.key);
if (cmp < 0) {
t = t.left;
} else if (cmp > 0) {
t = t.right;
} else {
t.setValue(value == null ? key : value);
return;
}
} while (t != null);
RBNode e = new RBNode<>(key, value == null ? key : value, parent);
//如果比较最终落在左子树,则直接将父节点左指针指向e
if (cmp < 0) {
parent.left = e;
}
//如果比较最终落在右子树,则直接将父节点右指针指向e
else {
parent.right = e;
}
//调整
fixAfterPut(e);
}
private void fixAfterPut(RBNode x) {
x.color = RED;
//本质上就是父节点是黑色就不需要调整,对应情况就是2,3
while (x != null && x != root && x.parent.color == RED) {
//1、x的父节点是爷爷的左孩子(左3)
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//叔叔节点
RBNode y = rightOf(parentOf(parentOf(x)));
//第3种情况
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//爷爷节点递归
x=parentOf(parentOf(x));
}
//第2种情况
else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
leftRotate(x);
}
//父亲变黑
setColor(parentOf(x), BLACK);
//爷爷变红
setColor(parentOf(parentOf(x)), RED);
//根据爷爷节点右旋转
rightRotate(parentOf(parentOf(x)));
}
}
//2、跟第一种情况相反操作
else {
//右3
//叔叔节点
RBNode y = leftOf(parentOf(parentOf(x)));
//第3种情况
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//爷爷节点递归
x=parentOf(parentOf(x));
}
//第2种情况
else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rightRotate(x);
}
//父亲变黑
setColor(parentOf(x), BLACK);
//爷爷变红
setColor(parentOf(parentOf(x)), RED);
//根据爷爷节点右旋转
leftRotate(parentOf(parentOf(x)));
}
}
}
root.color=BLACK;
}
public V remove(K key){
RBNode node=getNode(key);
if(node==null){
return null;
}
V oldValue = (V) node.value;
deleteNode(node);
return oldValue;
}
/**
* 删除操作:
* 1、删除叶子节点,直接删除
* 2、删除的节点有一个子节点,那么用子节点来替代
* 3、如果删除的节点有2个子节点,此时需要找到前驱节点或者后继节点来替代
*
* @param node
*/
private void deleteNode(RBNode node){
//3、node节点有2个孩子
if(node.left!=null&&node.right!=null){
//后继节点替代
RBNode rep = successor(node);
//前驱节点替代
// RBNode rep= predecessor(node);
node.key = rep.key;
node.value=rep.value;
node=rep;
}
RBNode replacement=node.left!=null?node.left:node.right;
//2、替代节点不为空
if(replacement!=null){
//替代者的父指针指向的原来node的父亲
replacement.parent=node.parent;
//node是根节点
if(node.parent==null){
root=replacement;
}
//node是左孩子,所以替代者依然是左孩子
else if(node==node.parent.left){
node.parent.left=replacement;
}
//node是右孩子,所以替代者依然是右孩子
else{
node.parent.right=replacement;
}
//将node的左右孩子指针和父指针都指向null(此时node处于游离状态,等待垃圾回收)
node.left=node.right=node.parent=null;
//替换完之后需要调整平衡
if(node.color==BLACK){
//需要调整,这种情况一定是红色(替代节点一定是红色,此时只要变色)
fixAfterRemove(replacement);
}
}
//删除节点就是根节点 这里有点问题?
else if(node.parent==null){
root=null;
}
//1、node节点是叶子节点,replacement为null
else{
//先调整
if(node.color==BLACK) {
fixAfterRemove(node);
}
//再删除
if(node.parent!=null){
if(node==node.parent.left){
node.parent.left=null;
}
else if(node==node.parent.right){
node.parent.right=null;
}
node.parent=null;
}
}
}
/**
* 删除后调整
* @param x
*/
private void fixAfterRemove(RBNode x){
while(x!=root&&colorOf(x)==BLACK){
//x是左孩子的情况
if(x==leftOf(parentOf(x))) {
//兄弟节点
RBNode rnode = rightOf(parentOf(x));
//判断此时兄弟节点是否是真正的兄弟节点
if(colorOf(rnode)==RED){
setColor(rnode,BLACK);
setColor(parentOf(x),RED);
leftRotate(parentOf(x));
//找到真正的兄弟节点
rnode=rightOf(parentOf(x));
}
//情况三,找兄弟借,兄弟没得借
if(colorOf(leftOf(rnode))==BLACK&&colorOf(rightOf(rnode))==BLACK){
//情况复杂,暂时不写
setColor(rnode,RED);
x=parentOf(x);
}
//情况二,找兄弟借,兄弟有的借
else{
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if(colorOf(rightOf(rnode))==BLACK){//这里已经是判断出来,一定有一个节点,那么右孩子为空,左孩子一定不为空
setColor(leftOf(rnode),BLACK);
setColor(rnode,RED);
rightRotate(rnode);
rnode=rightOf(parentOf(x));
}
setColor(rnode,colorOf(parentOf(x)));
setColor(parentOf(x),BLACK);
setColor(rightOf(rnode),BLACK);
leftRotate(parentOf(x));
x=root;
}
}
//x是右孩子的情况
else{
//兄弟节点
RBNode rnode = leftOf(parentOf(x));
//判断此时兄弟节点是否是真正的兄弟节点
if(colorOf(rnode)==RED){
setColor(rnode,BLACK);
setColor(parentOf(x),RED);
rightRotate(parentOf(x));
//找到真正的兄弟节点
rnode=leftOf(parentOf(x));
}
//情况三,找兄弟借,兄弟没得借
if(colorOf(rightOf(rnode))==BLACK&&colorOf(leftOf(rnode))==BLACK){
//情况复杂,暂时不写
setColor(rnode,RED);
x=parentOf(x);
}
//情况二,找兄弟借,兄弟有的借
else{
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if(colorOf(leftOf(rnode))==BLACK){
setColor(rightOf(rnode),BLACK);
setColor(rnode,RED);
leftRotate(rnode);
rnode=leftOf(parentOf(x));
}
setColor(rnode,colorOf(parentOf(x)));
setColor(parentOf(x),BLACK);
setColor(leftOf(rnode),BLACK);
rightRotate(parentOf(x));
x=root;
}
}
}
//情况一、替代节点是红色,则直接染红,补偿删除的黑色节点,这样红黑树依然保持平衡
setColor(x,BLACK);
}
private RBNode getNode(K key){
RBNode node=this.root;
while(node!=null){
int cmp = key.compareTo((K) node.key);
if(cmp<0){
node=node.left;
}
else if(cmp>0){
node=node.right;
}
else
return node;
}
return null;
}
static class RBNode, V> {
private RBNode parent;
private RBNode left;
private RBNode right;
private boolean color;
private K key;
private V value;
public RBNode() {
}
public RBNode(K key, V value, RBNode parent) {
this.parent = parent;
this.color = BLACK;
this.key = key;
this.value = value;
}
public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) {
this.parent = parent;
this.left = left;
this.right = right;
this.color = color;
this.key = key;
this.value = value;
}
public RBNode getParent() {
return parent;
}
public void setParent(RBNode parent) {
this.parent = parent;
}
public RBNode getLeft() {
return left;
}
public void setLeft(RBNode left) {
this.left = left;
}
public RBNode getRight() {
return right;
}
public void setRight(RBNode right) {
this.right = right;
}
public boolean isColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
}