红黑树分析
红黑树有五个性质:
性质1:节点是红色或黑色。
性质2:根节点是黑色。
性质3: 每个叶节点(NIL节点,空节点)是黑色的。
性质4: 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5: 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
三大核心操作:染色,左旋,右旋
插入情况分析:
-
父节点为null,则此节点为root
-
父节点为黑节点,直接插入
-
父节点为红,并且为爷爷节点的左子树,叔叔是红;
将爸爸和叔叔染黑,爷爷染红,以爷爷为节点进行递归(此时爷爷以下已经完整,爷爷以上不完整,把爷爷当作新插入的红色节点)
-
父节点为红节点,并且为爷爷节点的左子树,叔叔是黑或者Null;
- 当前节点为父亲节点的左子树LL操作。
- 为右节点LR,先右旋就从LR变成了LL,再LL操作;
-
父节点为红节点,并且为爷爷节点的右子树,叔叔是黑或者Null;
- 当前节点为父亲节点的右子树RR操作。
- 当前节点为父亲节点的左子树RL操作。
LL 操作:
RR 同LL, RL 同LR;
/** 1. 创建RBTree, 定义颜色
* 2. 创建RBNode
* 3. 辅助方法: parentOf(node), isRed(node),isBlack(node) setRed(node), setBlack(node),inOrderPrint()
* 4. 左旋方法: leftRotate(node)
* 5. 右旋方法: rightRotate(node)
* 6. 公开插入接口方法定义: insert(K key, V value);
* 7. 内部插入接口方法定义: insert(RBNode node);
* 8. 修正插入导致红黑树失衡方法定义: insertFIxUp(RBNode node);
* HashMap 底层是 链表+数组+红黑树
* @param <K> 键
* @param <V> 值
*/
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
// 定义根
private RBNode 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 void setRed(RBNode node){
if(node != null){
node.color = RED;
}
}
// 设置节点为黑色
private void setBlack(RBNode node){
if(node != null){
node.color = BLACK;
}
}
// 判断节点是否为红色
private boolean isBlack(RBNode node){
if(node != null){
return node.color == BLACK;
}
return false;
}
// 中序打印二叉树
public void inOrderPrint(){
inOrderPrint(this.root);
}
private void inOrderPrint(RBNode root){
if(root != null){
inOrderPrint(root.left);
System.out.println("Key: " + root.key + ",value: " + root.value);
inOrderPrint(root.right);
}
}
// 左旋
private void leftRotate(RBNode x){
RBNode y = x.right;
// 将x 的 右子节点 指向y的左子节点,并更新 y 做儿子的父亲为x
x.right = y.left;
if(y.left != null){
y.left.parent = x;
}
// 当x 的父亲不为null时, 将 y 代替x
if(x.parent != null){
y.parent = x.parent;
if(x == x.parent.left){
x.parent.left = y;
}else x.parent.right = y;
}else{
// 说明是根节点
this.root = y;
this.root.parent = null;
}
// 将x 变为 y的 左儿子
x.parent = y;
y.left = x;
}
// 右旋 , 同左旋
private void rightRotate(RBNode y){
RBNode x = y.left;
y.left = x.right;
if(x.right != null){
x.right.parent = y;
}
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;
}
x.right = y;
y.parent = x;
}
// 公开的插入方法
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 则 右子树,如果相等 则替换值,否则左子树
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;
// 判断 node 是否与 parent key 谁大
if(parent != null){
// 不会有等于的情况
int cmp = node.key.compareTo(parent.key);
if(cmp > 0){
parent.right = node;
}else {
parent.left = node;
}
}else this.root = node;
// 调用修复红黑树平衡方法
insertFixUp(node);
}
// 修复红黑树平衡
// |---- 1. 红黑树为空树 --- 将节点染黑
// |---- 2. 插入节点的key已存在 --- 不用处理了
// |---- 3. 插入节点的父节点为黑色 ---- 不用处理了
// |---- 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 双红 --- 先以父亲为节点右旋 变成 rr,再同rr操作
private void insertFixUp(RBNode node){
this.root.setColor(BLACK);
RBNode parent = parentOf(node);
RBNode gparent = parentOf(parent);
// 情景4:父节点为红
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.1
if(uncle == null || isBlack(uncle)){
// 4.2.1 LL情况
if(node == parent.left){
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
return ;
}
// 4.2.2 LR情况
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)){
if(node == parent.right){ // rr
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
if(node == parent.left){ // rl
rightRotate(parent);
insertFixUp(parent);
return ;
}
}
}
}
}
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) {
this.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) {
this.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;
}
}
}
删除操作: 一样很复杂,找第一个后继结点。
也可不做删除,做个标记,代表删除?