关于红黑树的左旋右旋
Posted 开源java学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于红黑树的左旋右旋相关的知识,希望对你有一定的参考价值。
刚刚看篇文章,是讲清华毕业去面试被面试官各种嫌弃,还..放网上了。认为红黑树都不会写居然应届毕业要18k...
红-黑树的特征
有如下两个特征:
①、节点都有颜色;
②、在插入和删除的过程中,要遵循保持这些颜色的不同排列规则。
第一个很好理解,在红-黑树中,每个节点的颜色或者是黑色或者是红色的。当然也可以是任意别的两种颜色,这里的颜色用于标记,我们可以在节点类Node中增加一个boolean型变量isRed,以此来表示颜色的信息。
第二点,在插入或者删除一个节点时,必须要遵守的规则称为红-黑规则:
1.每个节点不是红色就是黑色的;
2.根节点总是黑色的;
3.如果节点是红色的,则它的子节点必须是黑色的(反之不一定),(也就是从每个叶子到根的所有路径上不能有两个连续的红色节点);
4.从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
从根节点到叶节点的路径上的黑色节点的数目称为黑色高度,规则 4 另一种表示就是从根到叶节点路径上的黑色高度必须相同。
注意:新插入的节点颜色总是红色的,这是因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小,原因是插入黑色节点总会改变黑色高度(违背规则4),但是插入红色节点只有一半的机会会违背规则3(因为父节点是黑色的没事,父节点是红色的就违背规则3)。另外违背规则3比违背规则4要更容易修正。当插入一个新的节点时,可能会破坏这种平衡性,那么红-黑树是如何修正的呢?
2、红-黑树的自我修正
红-黑树主要通过三种方式对平衡进行修正,改变节点颜色、左旋和右旋。
①、改变节点颜色
新插入的节点为15,一般新插入颜色都为红色,那么我们发现直接插入会违反规则3,改为黑色却发现违反规则4。这时候我们将其父节点颜色改为黑色,父节点的兄弟节点颜色也改为黑色。通常其祖父节点50颜色会由黑色变为红色,但是由于50是根节点,所以我们这里不能改变根节点颜色。
②、右旋
首先要说明的是节点本身是不会旋转的,旋转改变的是节点之间的关系,选择一个节点作为旋转的顶端,如果做一次右旋,这个顶端节点会向下和向右移动到它右子节点的位置,它的左子节点会上移到它原来的位置。右旋的顶端节点必须要有左子节点。
③、左旋
左旋的顶端节点必须要有右子节点。
注意:我们改变颜色也是为了帮助我们判断何时执行什么旋转,而旋转是为了保证树的平衡。光改变节点颜色是不能起到任何作用的,旋转才是关键的操作,在新增节点或者删除节点之后,可能会破坏二叉树的平衡,那么何时执行旋转以及执行什么旋转,这才是要点。
3、左旋和右旋代码
①、节点类
节点类和二叉树的节点类差不多,只不过在其基础上增加了一个 boolean 类型的变量来表示节点的颜色。
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;
}
//打印节点的关键值和颜色信息
public String toString(){
return ""+key+(this.color == RED ? "R":"B");
}
}
②、左旋的具体实现
/********对红黑树节点x进行左旋操作 ************/
/*
* 左旋示意图:对节点x进行左旋
* p p
* / /
* x y
* / /
* lx y -----> x ry
* / /
* ly ry lx ly
* 左旋做了三件事:
* 1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)
* 2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)
* 3. 将y的左子节点设为x,将x的父节点设为y
*/
private void leftRotate(RBNode<T> x){
//1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)
RBNode<T> y = x.right;
x.right = y.left;
if(y.left != null){
y.left.parent = x;
}
//2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)
y.parent = x.parent;
if(x.parent == null){
this.root = y;//如果x的父节点为空(即x为根节点),则将y设为根节点
}else{
if(x == x.parent.left){//如果x是左子节点
x.parent.left = y;//则也将y设为左子节点
}else{
x.parent.right = y;//否则将y设为右子节点
}
}
//3. 将y的左子节点设为x,将x的父节点设为y
y.left = x;
x.parent = y;
}
③、右旋的具体实现
/*******对红黑树节点y进行右旋操作 **********/
/*
* 左旋示意图:对节点y进行右旋
* p p
* / /
* y x
* / /
* x ry -----> lx y
* / /
* lx rx rx ry
* 右旋做了三件事:
* 1. 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时)
* 2. 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右)
* 3. 将x的右子节点设为y,将y的父节点设为x
*/
private void rightRotate(RBNode<T> y){
//1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)
RBNode<T> x = y.left;
y.left = x.right;
if(x.right != null){
x.right.parent = y;
}
//2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)
x.parent = y.parent;
if(y.parent == null){
this.root = x;//如果y的父节点为空(即y为根节点),则旋转后将x设为根节点
}else{
if(y == y.parent.left){//如果y是左子节点
y.parent.left = x;//则将x也设置为左子节点
}else{
y.parent.right = x;//否则将x设置为右子节点
}
}
//3. 将x的左子节点设为y,将y的父节点设为y
x.right = y;
y.parent = x;
}
以上是关于关于红黑树的左旋右旋的主要内容,如果未能解决你的问题,请参考以下文章