通过学习某up主的红黑树教学视频,整理了相关知识和代码
相关知识点在代码注释中标出
红黑树构造:
public class RBTree <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;
}
//获取当前结点的父节点
private RBNode parentof(RBNode node){
if(node!=null){
return node.Parent;
}
return null;
}
//结点是否为红色
private boolean IsRed(RBNode node){
if(node!=null){
return node.color==RED;
}
return false;
}
//结点是否为黑色
private boolean IsBlack(RBNode node){
if(node!=null){
return node.color==BLACK;
}
return false;
}
//设置结点为红色
private void SetRed(RBNode node){
if(node!=null){
node.color=RED;
}
}
//设置结点为黑色
private void SetBlack(RBNode node){
if(node!=null){
node.color=BLACK;
}
}
//中序打印二叉树
public void inOrderPrint(){
inOrderPrint(this.root);
}
private void inOrderPrint(RBNode node){
if(node!=null){
inOrderPrint(node.left);
System.out.println("key:"+ node.key + ",value:" + node.value);
inOrderPrint(node.right);
}
}
//公开的插入方法
public void insert(K key,V value){
RBNode node=new RBNode();
node.setKey(key);
node.setValue(value);
//新节点一定是红色
node.setColor(RED);
insert(node);
}
private void insert(RBNode node){
RBNode parent=null;
RBNode x=this.root;
while (x!=null){
parent=x;
//cmp大于0,说明node.key大于x.key需要到x的右子树查找
//等于0时,说明进行替换操作
//小于0 说明到左子树查找
int cmp=node.key.compareTo(x.key);
if(cmp>0){
x=x.right;
}else if(cmp==0){
x.setValue(node.getValue());
return;
}else {
x=x.left;
}
}
node.Parent= parent;
if(parent!=null){
//判断node和parent谁的key更大
int cmp=node.key.compareTo(parent.key);
if(cmp>0){//当前node的key比parent大,需要把node放入parent的右子节点
parent.right=node;
}else {
parent.left=node;
}
}else {
this.root=node;
}
//需要调用修复红黑树平衡方法
insertfixup(node);
}
/***
* 红黑树修复方法
* 情景一:红黑树为空树,将根节点染色为黑色
* 情景二:插入结点的key已经存在,上面已经处理,不用处理
* 情景三:插入结点的父节点为黑色,依旧平衡,不用处理
*
* 情景四:插入结点的父节点为红色
* 情景4.1:叔叔结点存在,并且为红色(父叔双红)----将爸爸和叔叔染色为黑色,将爷爷染色为红色,并且以爷爷结点为当前结点
* 情景4.2:叔叔结点不存在,或者为黑色,父亲结点为爷爷结点的左子树
* 情景4.2.1:插入结点为其父节点的左子节点(LL情况)----将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷结点右旋,
* 情景4.2.2:插入结点为去父节点的右子节点(LR情况)-----以爸爸结点进行一次左旋,得到LL情景,然后指定爸爸结点为当前结点
* 情景4.3:叔叔结点不存在,或者为黑色,父节点为爷爷结点的右子树
* 情景4.3.1:插入结点为其父节点的右子节点(RR情况)-----将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷结点为当前结点,
* 情景4.2.2:插入结点为去父节点的左子节点(RL情况)-----以爸爸结点进行一次右旋,得到RR双红的情景,然后指定爸爸结点为当前结点
*/
private void insertfixup(RBNode node){
this.root.setColor(BLACK);
RBNode parent=parentof(node);
RBNode gparent=parentof(parent);
//情景四:插入结点的父节点为红色
if(parent!=null && IsRed(parent)){
//如果爸爸为红色,则一定存在爷爷结点
RBNode uncle=null;
if(parent==gparent.left){
uncle=gparent.right;
// 情景4.1:叔叔结点存在,并且为红色(父叔双红)----将爸爸和叔叔染色为黑色,将爷爷染色为红色,并且以爷爷结点为当前结点
if(uncle!=null && IsRed(uncle)){
SetBlack(parent);
SetBlack(uncle);
SetRed(gparent);
insertfixup(gparent);
return;
}
//情景4.2:叔叔结点不存在,或者为黑色,父亲结点为爷爷结点的左子树
if(uncle==null || IsBlack(uncle)){
//情景4.2.1:插入结点为其父节点的左子节点(LL情况)----将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷结点右旋,
if(node==parent.left){
SetBlack(parent);
SetRed(gparent);
rightRotate(gparent);
return;
}
//情景4.2.2:插入结点为去父节点的右子节点(LR情况)-----以爸爸结点进行一次左旋,得到LL情景,然后指定爸爸结点为当前结点
if(node==parent.right){
leftRotate(parent);
insertfixup(parent);
return;
}
}
}else {//父节点为右子树
uncle=gparent.left;
// 情景4.1:叔叔结点存在,并且为红色(父叔双红)----将爸爸和叔叔染色为黑色,将爷爷染色为红色,并且以爷爷结点为当前结点
if(uncle!=null && IsRed(uncle)){
SetBlack(parent);
SetBlack(uncle);
SetRed(gparent);
insertfixup(gparent);
return;
}
//情景4.3:叔叔结点不存在,或者为黑色,父节点为爷爷结点的右子树
if(uncle==null || IsBlack(uncle)){
//情景4.3.1:插入结点为其父节点的右子节点(RR情况)-----将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷结点为当前结点,
if(node==parent.right){
SetBlack(parent);
SetRed(gparent);
leftRotate(gparent);
return;
}
//情景4.2.2:插入结点为去父节点的左子节点(RL情况)-----以爸爸结点进行一次右旋,得到RR双红的情景,然后指定爸爸结点为当前结点
if(node==parent.left){
rightRotate(parent);
insertfixup(parent);
return;
}
}
}
}
}
/***
*
* p p
* | |
* x ------> y
* / \ / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
* 左旋x
* 1.并将x的右子节点指向y的左子节点(ly),将y的左子节点的父亲结点更新为x,
* 2.当x的父节点(不为空时),更新y的父结点为x的父节点,并将x的父节点指定子树(当前x的子树位置)指定为y
* 3.将x的父子节点更新为y,将y的左子节点更新为x
*/
private void leftRotate(RBNode x){
RBNode y=x.right;
//将x的右子节点指向y的左子节点(ly),将y的左子节点的父亲结点更新为x
x.right=y.left;
if(y.left!=null){
y.left.Parent=x;
}
//2.当x的父节点(不为空时),更新y的父结点为x的父节点,并将x的父节点指定子树(当前x的子树位置)指定为y
if(x.Parent!=null){
y.Parent=x.Parent;
if(x==x.Parent.left){
x.Parent.left=y;
}else {
x.Parent.right=y;
}
}else {//x为根节点,需要更新y为根节点
this.root=y;
this.root.Parent=null;
}
//3.将x的父子节点更新为y,将y的左子节点更新为x
x.Parent=y;
y.left=x;
}
/***
*
* p p
* | |
* y ------> x
* / \ / \
* x ry lx y
* / \ / \
* lx ly ly ry
*
* 右旋x
* 1.并将y的左子节点指向x的右子节点(ly),将x的右子节点的父亲结点更新为y,
* 2.当y的父节点(不为空时),更新x的父结点为y的父节点,并将y的父节点指定子树(当前y的子树位置)指定为x
* 3.将y的父子节点更新为x,将x的右子节点更新为y
*/
private void rightRotate(RBNode y){
RBNode x=y.left;
//1.并将y的左子节点指向x的右子节点(ly),将x的右子节点的父亲结点更新为y,
y.left=x.right;
if(x.right!=null){
x.right.Parent=y;
}
//2.当y的父节点(不为空时),更新x的父结点为y的父节点,并将y的父节点指定子树(当前y的子树位置)指定为x
if(y.Parent!=null){
x.Parent=y.Parent;
if(y==y.Parent.left){
y.Parent.left=x;
}else{
y.Parent.right=x;
}
}else {
this.root=x;
this.root.Parent=null;
}
//3.将y的父子节点更新为x,将x的右子节点更新为y
y.Parent=x;
x.right=y;
}
static class RBNode<K extends Comparable<K>,V>{
private RBNode Parent;
private RBNode left;
private RBNode right;
private boolean color;
private K key;
private V value;
public RBNode() {
}
public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) {
Parent = parent;
this.left = left;
this.right = right;
this.color = color;
this.key = key;
this.value = value;
}
public RBNode getParent() {
return Parent;
}
public void setParent(RBNode parent) {
Parent = parent;
}
public RBNode getLeft() {
return left;
}
public void setLeft(RBNode left) {
this.left = left;
}
public RBNode getRight() {
return right;
}
public void setRight(RBNode right) {
this.right = right;
}
public boolean isColor() {
return color;
}
public void setColor(boolean color) {
this.color = color;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
}
红黑树显示:
public class TreeOperation {
//获取树的层数
public static int getTreeDepth(RBTree.RBNode root){
return root==null? 0:(1+Math.max(getTreeDepth(root.getLeft()),getTreeDepth(root.getRight())));
}
private static void writeArray(RBTree.RBNode currNode,int rowIndex,int columnIndex,String[][]res,int treeDepth){
//保证树不为空
if(currNode==null){
return;
}
//先将当前结点保存在二维数组中
res[rowIndex][columnIndex]=String.valueOf(currNode.getKey()+"-"+(currNode.isColor()?"R":"B")+"");
//计算当前位于树的第几层
int currLevel=(rowIndex+1)/2;
//若到了最后一层,则返回
if(currLevel==treeDepth){
return;
}
//计算当前行下一行,每个元素之间的间隔(下一行的索引与当前元素的列索引之间的间隔)
int gap=treeDepth-currLevel-1;
//对左儿子进行判断,若有左儿子,则记录相应“/”和左儿子的值
if(currNode.getLeft()!=null){
res[rowIndex+1][columnIndex-gap]="/";
writeArray(currNode.getLeft(),rowIndex+2,columnIndex-gap*2,res,treeDepth);
}
//对右儿子进行判断,若有右儿子,则记录相应的“\”和右儿子的值
if(currNode.getRight()!=null){
res[rowIndex+1][columnIndex+gap]="\\";
writeArray(currNode.getRight(),rowIndex+2,columnIndex+gap*2,res,treeDepth);
}
}
public static void show(RBTree.RBNode root){
if(root==null){
System.out.println("空树");
}
//得到树的深度
int treeDepth=getTreeDepth(root);
//最后一行宽度为2的(n-1)次方乘3再加一
//作为整个二维数组的宽度
int arrayHeight=treeDepth*2-1;
int arrayWidth=(2<<(treeDepth-2))*3+1;
//用一个字符串数组来存储每个位置应该显示的元素
String[][]res=new String[arrayHeight][arrayWidth];
//对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++) {
for (int j = 0; j < arrayWidth; j++) {
res[i][j]=" ";
}
}
//从根节点开始,递归处理整个树
writeArray(root,0,arrayWidth/2,res,treeDepth);
//此时,已经将所有需要显示的元素存储到了二维数组中,将其拼接打印即可
for (String[]line:res){
StringBuilder sb=new StringBuilder();
for (int i = 0; i < line.length; i++) {
sb.append(line[i]);
if(line[i].length()>1 && i<=line.length-1){
i+=line[i].length()>4?2:line[i].length()-1;
}
}
System.out.println(sb.toString());
}
}
}
主程序调用:
public class RBTreeTest {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
RBTree<String,Object> rbTree=new RBTree<>();
while (true){
System.out.println("请输入key:");
String key=scanner.next();
if(key.equals("dd"))
break;
System.out.println();
rbTree.insert(key,null);
TreeOperation.show(rbTree.getRoot());
}
System.out.println("end");
}
}