标签:没事 inf 直接 就是 style dfa 键值 如何 while循环
概述
搜索二叉树在插入的数据是有序的时候会非常不平衡,几乎变成了线性结构,如插入数据顺序为10,20,30,40,50,那么该二叉树的结构会如下图所示,那么这样就和链表没啥区别,查找的时间复杂度就为O(n),而不是O(logN),为了以较快的时间搜索一颗树,我们就要保证这颗树的平衡性,也就是树的左右子树节点个数趋近相等,红黑树就是这样一颗平衡树,红黑树对插入的数据项会进行检查,如果插入项破坏了树的平衡,树会自动修复。

1.1红黑树特征
1 树中节点非黑及红
2 插入和删除节点必须遵循红黑规则:
1.每个节点不是红色就是黑色的;
2.根节点总是黑色的;
3.如果节点是红色的,则它的子节点必须是黑色的(反之不一定),(也就是从每个叶子到根的所有路径上不能有两个连续的红色节点);
4.从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
注意:新插入的节点颜色总是红色的,这是因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小,原因是插入黑色节点总会改变黑色高度(违背规则4),但是插入红色节点只有一半的机会会违背规则3(因为父节点是黑色的没事,父节点是红色的就违背规则3)另外违背规则3比违背规则4要更容易修正。当插入一个新的节点时,可能会破坏这种平衡性,那么红-黑树是如何修正的呢?
1.2 红黑树自我修正方法
红黑树节点实体类
public class RBNode<T extends Comparable<T>> {
boolean color;//颜色
T key;//关键值
RBNode<T> left;//左子节点
RBNode<T> right;//右子节点
RBNode<T> parent;//父节点
public RBNode(boolean color,T key,RBNode<T> parent,RBNode<T> left,RBNode<T> right){
this.color = color;
this.key = key;
this.parent = parent;
this.left = left;
this.right = right;
}
//获得节点的关键值
public T getKey(){
return key;
}
}
1.2.1改变节点颜色
新插入的节点为15,一般新插入颜色都为红色,那么我们发现直接插入会违反规则3,改为黑色却发现违反规则4。这时候我们将其父节点颜色改为黑色,父节点的兄弟节点颜色也改为黑色。通常其祖父节点50颜色会由黑色变为红色,但是由于50是根节点,所以我们这里不能改变根节点颜色。

1.2.2 右旋
首先要说明的是节点本身是不会旋转的,旋转改变的是节点之间的关系,选择一个节点作为旋转的顶端,如果做一次右旋,这个顶端节点会向下和向右移动到它右子节点的位置,它的左子节点会上移到它原来的位置。右旋的顶端节点必须要有左子节点。

1.2.3 左旋

左旋代码实现(右旋正好相反,原理一样):
/**
* 以C为轴做左旋
* 左旋操作做了三件事
* 1 将的B的左子节点赋给C的右子节点,并将C赋予B的左子节点
* 2 将C的父节点A赋给B的父节点,同时更新A的子节点为B(区分左右)
* 3 将B的左子节点设置为C,将C父节点设置为B
* @param C
*/
private void leftRotate(RBNode<T> C){
RBNode<T> root;
RBNode<T> B = C.right;
/* 1 将的B的左子节点赋给C的右子节点,
并将B的左子节点父节点指向C(B的左子节点不为空)
*/
C.right = B.left;
if(B.left != null){
B.left.parent=C;
}
/*
2 将C的父节点A赋给B的父节点,同时更新A的子节点为B(区分左右)
*/
B.parent = C.parent;
if(C.parent == null){
root=B;//如果C为根节点则将B设置为根
}else{
if(C == C.parent.left){
C.parent.left = B;
}else{
C.parent.right = B;
}
}
//3 将B的左子节点设置为C,将C父节点设置为B
B.left = C;
C.parent = B;
}
2 插入操作
和搜索二叉树一样,都是都是得先找到插入的位置,然后再将节点插入,最后利用旋转操作修正红黑树;修正的过程要分情况讨论。
1:如果是第一次插入,由于原树为空,所以只会违反红-黑树的规则2,所以只要把根节点涂黑即可;
2:如果插入节点的父节点是黑色的,那不会违背红-黑树的规则,什么也不需要做;
但是遇到如下三种情况,我们就要开始变色和旋转了:
①、插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色。
②、插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的右子节点。
③、插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的左子节点。
下面我们挨个分析这三种情况都需要如何操作,然后给出实现代码。
在下面的讨论中,使用N,P,G,U表示关联的节点。N(now)表示当前节点,P(parent)表示N的父节点,
U(uncle)表示N的叔叔节点,G(grandfather)表示N的祖父节点,也就是P和U的父节点。
①、插入节点的父节点和其叔叔节点均为红色。此时,肯定存在祖父节点,但是不知道父节点是其左子节点还是右子节点,但是由于对称性,我们只要讨论出一边的情况,另一种情况自然也与之对应。这里考虑父节点是其祖父节点的左子节点的情况,如下左图所示:
情况1

对于这种情况,我们要做的操作有:将当前节点(4) 的父节点(5) 和叔叔节点(8) 涂黑,将祖父节点(7)涂红,变成了上有图所示的情况。再将当前节点指向其祖父节点,再次从新的当前节点开始算法(具体看下面的步骤)。这样上右图就变成情况2了。
情况2

对于情况2:插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的右子节点。我们要做的操作有:将当前节点(7)的父节点(2)作为新的节点,以新的当前节点为支点做左旋操作。完成后如左下图所示,这样左下图就变成情况3了。
情况3

对于情况3:插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的左子节点。我们要做的操作有:将当前节点的父节点(7)涂黑,将祖父节点(11)涂红,在祖父节点为支点做右旋操作。最后把根节点涂黑,整个红-黑树重新恢复了平衡,如右上图所示。至此,插入操作完成!
修复完的红黑树

红黑树修复代码:
private void insertFixUp(RBNode<T> node){
RBNode<T> parent,gparent;//定义父节点和祖父节点
//需要修正的条件:父节点存在,且父节点的颜色是红色
while(((parent = parentOf(node)) != null) && isRed(parent)){
gparent = parentOf(parent);//获得祖父节点
//若父节点是祖父节点的左子节点,下面的else相反
if(parent == gparent.left){
RBNode<T> uncle = gparent.right;//获得叔叔节点
//case1:叔叔节点也是红色
if(uncle != null && isRed(uncle)){
setBlack(parent);//把父节点和叔叔节点涂黑
setBlack(gparent);
setRed(gparent);//把祖父节点涂红
node = gparent;//把位置放到祖父节点处
continue;//继续while循环,重新判断
}
//case2:叔叔节点是黑色,且当前节点是右子节点
if(node == parent.right){
leftRotate(parent);//从父节点出左旋
RBNode<T> tmp = parent;//然后将父节点和自己调换一下,为下面右旋做准备
parent = node;
node = tmp;
}
//case3:叔叔节点是黑色,且当前节点是左子节点
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
}else{//若父节点是祖父节点的右子节点,与上面的情况完全相反,本质是一样的
RBNode<T> uncle = gparent.left;
//case1:叔叔节点也是红色的
if(uncle != null && isRed(uncle)){
setBlack(parent);
setBlack(uncle);
setRed(gparent);
node = gparent;
continue;
}
//case2:叔叔节点是黑色的,且当前节点是左子节点
if(node == parent.left){
rightRotate(parent);
RBNode<T> tmp = parent;
parent = node;
node = tmp;
}
//case3:叔叔节点是黑色的,且当前节点是右子节点
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
}
setBlack(root);//将根节点设置为黑色
}
标签:没事 inf 直接 就是 style dfa 键值 如何 while循环
原文地址:https://www.cnblogs.com/sharing-java/p/10808206.html