红黑树
红黑树的5个特性:
1.每个节点要不黑色就是红色
2.根节点是黑色
3.每个叶子节点是黑色(NIL)
4.不能有两个 红色节点相连接,那么红色节点的 两个孩子节点必定 为黑色
5.黑高 任意节点出发到叶子节点 褐色节点数量相同
6.如果一个节点存在一个黑色子节点,那么必定该节点有两个孩子节点
红黑树的 节点属性:
1.颜色color
2.左孩子节点leftNode
3.右孩子节点rightNode
4.父亲节点 parent
5.Key value
左旋和右旋(核心)
左旋:
右旋:
插入算法:
1.类似于 二叉排序树 先查找到插入的节点位置,插入进去。之后再做 红黑树的平衡操作。(注意:相等的时候,直接 覆盖掉value值即可);
思路:
1.1.申明一个节点指向待插入位置的parent
1.2申明p用于遍历循环查找到待插入位置
1.3判断parent是否为空,如果为空 即红黑树为空树,直接插入变色,相反插入后进行平衡操作。
2.插入平衡操作(记住 在最后设置根节点为黑色)
情形一:红黑树为空树,(这个已经在插入已经实现)。
情形二: 插入节点的key已经存在,(在插入的时候已经完成覆盖)
情形三:插入节点的父节点为黑色,直接插入即可
情形四:(重点处理对象)插入节点的父节点为红色
|---情景4.1:叔叔节点存在,并且为红色(父-叔 双红)
* |---情景4.2:叔叔节点不存在,或者为黑色,父节点为爷爷节点的左子树
* |---情景4.2.1:插入节点为其父节点的左子节点(LL红情况)
1.Parent和gparent 交换颜色 2.右旋
* |---情景4.2.2:插入节点为其父节点的右子节点(LR红情况)
1.先以parent左旋,2.然后按照LL红处理
* |---情景4.3:叔叔节点不存在,或者为黑色,父节点为爷爷节点的右子树
* |---情景4.3.1:插入节点为其父节点的右子节点(RR红情况)
* |---情景4.3.2:插入节点为其父节点的左子节点(RL红情况)
删除操作:(复杂一些)
分三个方法:
1.查找待删除节点 searchRemoveNode
2.执行删除操作 removeNode
3.二叉树平衡操作 balanceRBTree
一.查找待删除节点
RBNode needRemoveNode = root;
while(needRemoveNode!=null)
{
if(needRemoveNode.key.compareTo (key)<0) // needRemoveNode<key
{
needRemoveNode = needRemoveNode.rightNode;
}
else if(needRemoveNode.key.compareTo (key)>0) //needRemoveNode>key
{
needRemoveNode = needRemoveNode.leftNode;
}
else //相等的情况 此时needRemoveNode 就是 待删除节点
{
break;
}
}
二.执行删除操作 (二叉排序树的删除操作)
* 1.无子节点
* 2.只有一个节点
* 3.有两个 ->转为 1 和 2
* 传进来的是 待删除节点 用于 情形3 转为 情形1 和 2 提供桥梁
主要处理的就是删除节点 是黑色的无孩子节点
private void removeNode(RBNode n)
{
/**
* 接下来就是删除节点 (思想是按照 二叉排序树)
* 1.无节点时候 1.1 红色 直接删除
* 1.2 黑色,需要平衡调整
* 2.只有一个节点 此时 n肯定为黑色 他的子节点必定为红色
* 3.有两个节点 转至 1 和2 情形
*/
// 1 无子节点时候
if(n.leftNode == null && n.rightNode == null)
{
// 1.1 删除节点为红色时候 此时不可能为根节点 因为root 一定为黑色
if(isRed (n))
{
if(n.parent.leftNode == n)
{
n.parent.leftNode = null;
}
else if(n.parent.rightNode == n)
{
n.parent.rightNode = null;
}
}
//*******删除节点为黑色的时候 需要平衡操作 仅仅此一处 调整异常麻烦********
else
{ //先进行平衡操作,然后再 删除该节点
balanceRBTree(n);
if(this.root == n)
{
this.root = null;
}
else
{
if(n.parent.leftNode == n){
n.parent.leftNode = null;
} else {
n.parent.rightNode = null;
}
}
}
}
/**
* 2.只有一个子节点的时候
* 此时删除节点只能为黑色,子节点一定为红色
*/
else if((n.leftNode != null && n.rightNode == null)||(n.leftNode == null && n.rightNode != null))
{
//查找那个不为空的子节点
RBNode replaceNode = n.leftNode != null ? n.leftNode : n.rightNode;
replaceNode.parent = n.parent;
//n可能为 根节点
if(replaceNode.parent == null)
{
root = replaceNode;
}
else if(n.parent.leftNode == n)
{
n.parent.leftNode = replaceNode;
}
else
{
n.parent.rightNode = replaceNode;
}
n.remove ();
//涂黑 达到平衡 这里 就可以直接返回
setBlack (replaceNode);
return;
}
/**
* 3. 有两个节点时候
* 可以转到 情形 1 和 2
*/
else
{
//最终这个p指向的就是 替换节点
RBNode p = n.leftNode;
while (p .rightNode!= null)
{
p = p.rightNode;
}
//替换值操作
n.key = p.key;
n.v = p.v;
//将 n 指向p 那么 n 变为待删除节点 转到了情形 1 和 2
n = p;
//递归遍历 实质上-> 情形三 转为 情形一 和 情形二
// 此时的 n 必定 要不 无子节点 要不 就一个子节点
removeNode (n);
}
}
3.2 S在右子树上 N在左子树上
变色操作 左旋 如以上操作