完整代码和测试
package 红黑树;
import com.sun.org.apache.regexp.internal.RE;
import org.omg.CORBA.UNKNOWN;
import 二叉排序树.TwoTree;
/**
* 红黑树 K:key(必须是可比较的 可以自定义比较规则) V:value
*
*性质1:每个节点要么是黑色,要么是红色。
*性质2:根节点是黑色。
*性质3:每个叶子节点(NIL)是黑色。
*性质4:每个红色节点的两个子节点一定都是黑色。
* 不能有两个红色节点相连。
*性质5:任意一节点到每个叶子节点的路径都包含数量相同的黑结点。俗称:黑高!
*性质6:如果一个节点存在黑子节点,那么该结点肯定有两个子节点
*/
/**
* 插入后修复红黑树平衡的方法
* |---情景1:红黑树为空树 变红为黑即可
* |---情景2:插入节点的key已经存在 直接替换Value即可
* |---情景3:插入节点的父节点为黑色
* 如果父亲为黑树 不需要调整(只需要判断以下几种情况)
*
* 情景4 需要咱们去处理
* |---情景4:插入节点的父节点为红色
* |---情景4.1:叔叔节点存在,并且为红色(父-叔 双红)
* 父亲 和 叔叔 变黑 爷爷变红 因为爷爷颜色变了 需要对爷爷 及其以上进行调整
* |---情景4.2:叔叔节点不存在,或者为黑色,父节点为爷爷节点的左子树
* |---情景4.2.1:插入节点为其父节点的左子节点(LL情况)
* 父亲变黑 爷爷变红 右旋 不需要再调整
* |---情景4.2.2:插入节点为其父节点的右子节点(LR情况)
* 左旋 成为 LL情况 再递归解决LL情况
* |---情景4.3:叔叔节点不存在,或者为黑色,父节点为爷爷节点的右子树
* |---情景4.3.1:插入节点为其父节点的右子节点(RR情况)
* |---情景4.3.2:插入节点为其父节点的左子节点(RL情况)
*/
public class RedAndBlackTree <K extends Comparable<K>,V>
{
// 红色节点 和 黑色节点
private static final boolean RED = true;
private static final boolean BLACK = false;
//红黑树的树根
private RBNode root;
public RBNode getRoot() {
return root;
}
/**
* 测试 用的
*/
public void insertNodes(K keys[])
{
for (int i =0;i<keys.length;i++)
{
RBNode node = new RBNode (RED,null,null,null,keys[i],null);
insertNode(node);
}
}
/***
* 插入算法
*/
public void insertNode(K key , V value)
{
RBNode node = new RBNode (RED,null,null,null,key,value);
insertNode(node);
}
/**
* 内部 核心插入算法
* @param node
*/
private void insertNode(RBNode node)
{
//parent 指向 被插入节点的父节点
RBNode parent = null;
RBNode p = root; //p 用于循环遍历的
while (p != null)
{
parent = p;
int num = node.key.compareTo (p.key);
if(num <0) // node < p
{
p = p.leftNode;
}
else if(num > 0)
{
p = p.rightNode;
}
else //相等情况下 就直接覆盖 value 即可
{
p.setV (node.v);
return;
}
}
//不为空 树 时候 parent指向待插入节点的父亲节点
node.parent = parent;
if(parent != null)
{
if(node.key.compareTo (parent.key)<0)
{
parent.leftNode = node;
}
else
{
parent.rightNode = node;
}
}
//代表 红黑树 一个节点都没有 即 空树情况
else
{
this.root = node;
this.root.setColor (BLACK);
return;
}
//插入之后需要进行修复红黑树,让红黑树再次平衡。
insertFixUp(node);
}
/**
* 平衡 红黑树
* @param node
*/
/**
* 插入后修复红黑树平衡的方法
* |---情景1:红黑树为空树 上一个方法已经实现 (变红为黑即可)
* |---情景2:插入节点的key已经存在 上一个方法已经实现 (直接替换即可)
* |---情景3:插入节点的父节点为黑色
* 如果父亲为黑树 不需要调整(只需要判断以下几种情况)
*
* 情景4 需要咱们去处理
* |---情景4:插入节点的父节点为红色
* |---情景4.1:叔叔节点存在,并且为红色(父-叔 双红)
* |---情景4.2:叔叔节点不存在,或者为黑色,父节点为爷爷节点的左子树
* |---情景4.2.1:插入节点为其父节点的左子节点(LL情况)
* |---情景4.2.2:插入节点为其父节点的右子节点(LR情况)
* |---情景4.3:叔叔节点不存在,或者为黑色,父节点为爷爷节点的右子树
* |---情景4.3.1:插入节点为其父节点的右子节点(RR情况)
* |---情景4.3.2:插入节点为其父节点的左子节点(RL情况)
*/
private void insertFixUp(RBNode node)
{
// node 插入进去的节点 parent:父亲节点 gparent:爷爷节点
RBNode parent = getParent (node);
RBNode gparent = getParent (parent);
//外层判断 父亲节点存在,且为红色,那么爷爷节点一定存在 且为黑色
if(parent != null && isRed (parent))
{
/**
* 里面分两大类情况: 父亲在爷爷的左边 还是 爷爷的右边
*/
//父亲在 左边情况
if(gparent.leftNode == parent)
{
//叔叔节点
RBNode uncle = gparent.rightNode;
//叔叔和父亲 双红情况下 先进行变色操作
if(uncle != null && isRed (uncle))
{
setBlack (parent);
setBlack (uncle);
setRed (gparent);
//对爷爷节点 再进行调整 因为爷爷节点变色 肯定对上层 有影响;
insertFixUp (gparent);
return;
}
// 叔叔节点 为黑色 或者 不存在 这时候 要分是 LL 情况 还是LR情况
if(uncle == null || !isRed (uncle))
{
//LL情况 变色 再 右旋
/**
* pp(黑) pp(红) p(黑)
* / \ / \ 右旋 / \
* p(红) -> p(黑) -> x(红) pp(红)
* / /
* x (红) x(红)
*/
//因为 此时 此模型的根节点 颜色(黑色 没变 因此不需要要 再次调整)
if(parent.leftNode == node)
{
setBlack (parent);
setRed (gparent);
rightRotate (gparent);
}
//LR情况 先对parent进行 左旋 -> LL情况 然后 再按LL情况处理;
if(parent.rightNode == node)
{
//这里一定要对 (父) parent 左旋
leftRotate (parent);
//左旋完成后 parent 成了 插入节点调正的地方了
/**
* gp(黑) gp(红)
* / \ / \
* parent(红) -> node(红)
* \ /
* node (红) parent(红)
*/
//下轮的 调整处理 LL 情况
insertFixUp (parent);
//这里的 return 加不加无所谓 因为上边 insertFixUp 完成了对根节点的 变黑操作
return;
}
}
}
//父亲节点 在 右边的时候
else
{
RBNode uncle = gparent.leftNode;
//4.1 父亲 和 叔叔 双红
if(uncle