左倾红黑树在经典红黑树上加了一个限制:红色结点只能是左孩子。建议学习红黑树从2-3树——左倾红黑树——经典红黑树学起,方便理解及进一步学习。下面记录一下我学红黑树的过程及记录:
一、左倾红黑树的定义
左倾红黑树是含有红黑链接并满足下列条件的二叉查找树:
- 红链接均为左链接;
- 没有任何一个结点同时和两条红链接相连;
- 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同;
下面是红黑树与2-3树的对应关系:
二、平衡化
在对红黑树进行一些增删改查的操作后,很有可能会出现红色的右链接或者两条连续红色的链接,而这些都不满足红黑树的定义,所以我们需要对这些情况通过旋转进行修复,让红黑树保持平衡。
1 、左旋
当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
前提:当前结点为h,它的右子结点为x;
左旋过程:
- 让x的左子结点变为h的右子结点:h.right=x.left;
- 让h成为x的左子结点:x.left=h;
- 让h的color属性变为x的color属性值:x.color=h.color;
- 让h的color属性变为RED:h.color=true;
2 、右旋
当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
前提:当前结点为h,它的左子结点为x;
右旋过程:
- 让x的右子结点成为h的左子结点:h.left = x.right;
- 让h成为x的右子结点:x.right=h;
- 让x的color变为h的color属性值:x.color = h.color;
- 让h的color为RED;
2 、颜色反转
当一个结点的左子结点和右子结点的color都为RED时,也就是出现了临时的4-结点,此时只需要把左子结点和右子结点的颜色变为BLACK,同时让当前结点的颜色变为RED即可。
红黑树API实现:
package com.xiaojie.tree;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* @author Mrli
* @date 2020/9/14 14:23
* 红黑树的实现:结点插入、删除、树深度、前中后序加层次遍历、或取最小最大值
*/
public class RedBlackTree<Key extends Comparable<Key>,Value>{
/**
* 红黑树结点
* @param <Key>
* @param <Value>
*/
private class Node<Key,Value> {
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
//由其父结点指向它的链接的颜色
public boolean color;
/**
* 新建结点构造函数
* @param key
* @param value
* @param left
* @param right
* @param color
*/
public Node(Key key, Value value, Node left, Node right, boolean color) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.color = color;
}
}
/**
*根节点
*/
private Node root;
/**
*记录树中元素的个数
*/
private int N;
/**
* 红色结点标识
*/
private static final boolean RED = true;
/**
* 黑色结点标识
*/
private static final boolean BLACK = false;
/**
* 判断当前结点的指向链接是否为红色
* @param x
* @return
*/
private boolean isRed(Node x){
if(x == null) {
return false;
}
return x.color == RED;
}
/**
* 左旋调整
* @param h
* @return
*/
private Node rotateLeft(Node h) {
//找出当前结点h的右子结点
Node x = h.right;
//让x的左子结点变为h的右子结点
h.right = x.left;
//让h成为x的左子结点
x.left = h;
//让h的color属性变为x的color属性值
x.color = h.color;
//让h的color属性变为RED
h.color = RED;
//返回左旋后的结点
return x;
}
/**
* 右旋调整
* @param h
* @return
*/
private Node rotateRight(Node h) {
//找出当前结点h的左子结点
Node x = h.left;
//让x的右子结点变为h的左子结点
h.left = x.right;
//让h成为x的右子结点
x.right = h;
//让h的color属性变为x的color属性值
x.color = h.color;
//让h的color属性变为RED
h.color = RED;
//返回右旋后的结点
return x;
}
/**
* 颜色反转,相当于完成拆分4-结点
* @param h
*/
private void flipColors(Node h) {
//当前结点的左右子结点的color属性值都变为黑色
h.left.color = BLACK;
h.right.color = BLACK;
//当前结点变为红色
h.color = RED;
}
/**
* 在整个树上完成插入操作
* @param key
* @param val
*/
public void put(Key key, Value val) {
//在root整个树上插入key-val,返回root
root = put(root,key,val);
//让根结点的颜色变为BLACK
root.color = BLACK;
}
/**
* 在指定树中,完成插入操作,并返回添加元素后新的树
* @param h
* @param key
* @param val
* @return
*/
private Node put(Node h, Key key, Value val){
//如果h为空,则为第一个结点
if(h == null) {
N++;
return new Node(key,val,null,null,RED);
}
//当前插入节点的key与root根节点key比较
int cmp = key.compareTo((Key) h.key);
if(cmp < 0) {
//插入到当前结点的左子树
h.left = put(h.left,