其实红黑树插入元素的过程和BST树没什么区别,主要是插入之后复原树的过程,要分三种情况对待。
情况一:要插入节点(D)的父节点(B)为红色,叔叔节点(C)为黑色,并且要插入的节点(D)和父节点(B)在同一侧。
这种情况,就要将待插入节点的父节点(B)和祖先节点(A)颜色互换,再以祖先节点(A)为基准进行右旋转或左旋转(如果父节点在祖先节点的左侧,那么进行右旋转;如果父节点在祖先节点的右侧,进行左旋转)
上图的情况,显然需要以祖先节点(A)为基准右旋转,一系列复原操作后的树将会是如下模样:
--------------------------------------------------------------------------------------------------------------------------------------
情况二:要插入节点(D)的父节点(B)为红色,叔叔节点(C)为黑色,并且要插入的节点(D)和父节点(B)不在同一侧。
这种情况,首先要以父亲节点(B)为基准进行左旋转或右旋转(保证待插入节点(D)和其父亲节点(B)在同一侧即可),上图显然要进行左旋转。
之后,重复情况1中的操作即可。
复原操作后的树将会是如下模样:
--------------------------------------------------------------------------------------------------------------------------------------
情况三:待插入节点(D)的父亲节点(B)和叔叔节点(C)都是红色。
这种情况,首先把父亲节点(B)和叔叔节点(C)都变成黑色,再把祖先节点(A)变成红色。
再以祖先节点(A)为当前节点cur向根节点遍历判断,直到当前节点cur为树根时,将当前节点染黑即可。
这种情况的复原树操作后,树将会是这样的模样:
--------------------------------------------------------------------------------------------------------------------------------------
此处应该有代码:
我就把包括节点类型的定义,左旋转和右旋转的实现一并发出来吧,方便大家自己测试。
左旋转和右旋转如果不太了解的话,之前我有写过一片关于红黑树左右旋转的具体操作:
https://blog.csdn.net/weixin_43729854/article/details/104579104
以下代码结合上述我分析的三种情况看,应该不难理解。
注意情况二和情况一的一并处理,需要在情况二进行第一部分操作后,将当前节点node重新指向离根节点最远的那个节点。
/**
* 定义枚举类型的节点颜色
*/
enum Color {
RED,
BLACK
}
/**
* 红黑树的实现
*
* @param <T>
*/
class RBTree<T extends Comparable<T>> {
/**
* 指向红黑树的根节点
*/
private Entry<T> root;
public RBTree() {
this.root = null;
}
/**
* 红黑树的插入操作
* @param val
*/
public void insert(T val){
//根节点是黑色
if(this.root==null){
this.root= new Entry<>(val,Color.BLACK);
return;
}
//搜索合适位置插入val
Entry<T> parent=null;
Entry<T> cur = this.root;
while(cur!=null){
parent = cur;
if(cur.data.compareTo(val)>0){
cur=cur.left;
}else if(cur.data.compareTo(val)<0){
cur=cur.right;
}else {
return;
}
}
//找到位置后插入node节点
Entry<T> node = new Entry<>(val,Color.RED);
//更新插入节点的parent
node.parent=parent;
if(parent.data.compareTo(val)>0){
parent.left=node;
}else {
parent.right=node;
}
//如果插入节点的父节点也是红色,破坏了红黑树性质,需要进行复原树操作
if(parent.color==Color.RED){
fixAfterInsert(node);
}
}
/**
* 红黑树的插入后复原树操作
* @param node
*/
private void fixAfterInsert(Entry<T> node) {
//判断待插入节点的父节点是否也是红色节点,如果是,进入循环来复原树。
while (color(parent(node))==Color.RED){
//如果插入节点在祖先节点的左侧
if(left(parent(parent(node)))==parent(node)){
//定义叔叔节点
Entry<T> uncle = right(parent(parent(node)));
//如果叔叔节点为红色
//图中情况三
if(color(uncle)==Color.RED){
//把父亲和叔叔节点都变为黑色
setColor(parent(node),Color.BLACK);
setColor(uncle,Color.BLACK);
//把祖先节点变为红色
setColor(parent(parent(node)),Color.RED);
//node指向祖先节点,继续向根节点遍历,检查是否还需要调整
node=parent(parent(node));
}else {
//处理图中 情况二 的调整在同一侧的部分
// 因为剩下的旋转部分和图中 情况一 的操作相同,所以之后一并处理
if(right(parent(node))==node){
//这一步是为了在 调整在同一侧 操作后,让node指向最远离根节点的那个节点
//换言之就是 让node的指向状态,和 情况一 中的指向状态相同,方便一并处理
node = parent(node);
leftRotate(node);
}
//处理图中 情况一
setColor(parent(node),Color.BLACK);
setColor(parent(parent(node)),Color.RED);
rightRotate(parent(parent(node)));
break;
}
}else {
//如果插入节点在祖先节点的右侧
//定义叔叔节点
Entry<T> uncle = left(parent(parent(node)));
//如果叔叔节点为红色
//图中情况三
if (color(uncle) == Color.RED) {
//把父亲和叔叔节点都变为黑色
setColor(parent(node), Color.BLACK);
setColor(uncle, Color.BLACK);
//把祖先节点变为红色
setColor(parent(parent(node)), Color.RED);
//node指向祖先节点,继续向根节点遍历,检查是否还需要调整
node = parent(parent(node));
} else {
//处理图中 情况二 的调整在同一侧的部分
// 因为剩下的旋转部分和图中 情况一 的操作相同,所以之后一并处理
if (left(parent(node)) == node) {
//这一步是为了在 调整在同一侧 操作后,让node指向最远离根节点的那个节点
//换言之就是 让node的指向状态,和 情况一 中的指向状态相同,方便一并处理
node = parent(node);
rightRotate(node);
}
//处理图中 情况一
setColor(parent(node), Color.BLACK);
setColor(parent(parent(node)), Color.RED);
leftRotate(parent(parent(node)));
break;
}
}
}
}
/**
*封装一些接口,方便实现复原树操作的编码
*/
private Entry<T> parent(Entry<T> node){
return node.parent;
}
private Entry<T> left(Entry<T> node){
return node.left;
}
private Entry<T> right(Entry<T> node){
return node.right;
}
private Color color(Entry<T> node){
return node.color;
}
private void setColor(Entry<T> node,Color color){
node.color=color;
}
/**
* 节点的左旋转操作
*
* @param node
*/
private void leftRotate(Entry<T> node) {
Entry<T> child = node.right;
child.parent = node.parent;
//判断node节点是否为根节点,如果是,那么child将成为根节点
if (null == node.parent) {
this.root = child;
} else {
//判断node节点是它父亲节点的左孩子还是右孩子,再进行移动
if (node.parent.left == node) {
node.parent.left = child;
} else {
node.parent.right = child;
}
}//以上为①操作
node.right = child.left;
if (null != child.left) {
child.left.parent = node;
}
//以上为②操作
child.left = node;
node.parent = child;
//以上为③操作
}
/**
* 节点的右旋转操作
*
* @param node
*/
private void rightRotate(Entry<T> node) {
Entry<T> child = node.left;
child.parent = node.parent;
if (null == node.parent) {
this.root = child;
} else {
if (node.parent.left == node) {
node.parent.left = child;
} else {
node.parent.right = child;
}
}
node.left = child.right;
if (null != child.right) {
child.right.parent = node;
}
child.right = node;
node.parent = child;
}
/**
* 节点类型定义
*
* @param <T>
*/
private static class Entry<T extends Comparable<T>> {
T data;
Entry<T> left;
Entry<T> right;
Entry<T> parent;
Color color;
public Entry(T data, Entry<T> left, Entry<T> right, Entry<T> parent, Color color) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
this.color = color;
}
public Entry(T data, Color color) {
this.data = data;
this.color = color;
}
}
}