一.简介
红黑树作为一种二叉搜索树的一种实现,红黑树的左右子树高度差可能大于 1。所以红黑树不是严格意义上的平衡二叉树(AVL),但红黑树是黑色节点完美平衡, 其平均统计性能要强于 AVL 。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。
1.节点是红色或黑色。
2.根节点是黑色。
3.每个红色节点的两个子节点都是黑色。(红色节点的子节点必须是黑色节点)
4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
故红黑树是黑色平衡的树,左子树与右子树高度差不会超过2倍。红节点的父节点、子节点只能是黑节点
如果插入节点是黑色则所在路径将多出一个黑色节点,故新插入节点是红色节点(插入/删除红色节点不会改变所在路径黑色节点数量),通过旋转和调整节点颜色保证树的平衡
二.实现
红黑树增加/删除节点时需要保证子树黑节点平衡,有如下情况:
1.插入节点为树的根节点时,把根节点颜色修改为黑色
2.插入节点父节点是黑色节点,由于插入节点是红色节点,不会影响该子树黑色节点数量,故不用任何处理
3.插入节点父节点为红色且父节点的兄弟节点也为红色,父节点、父节点的兄弟节点修改为黑色,父节点的父节点修改为红色,再以父节点的父节点为插入节点递归处理
4.父节点为红色且父节点的兄弟节点为null或为黑节点
由于父节点是红色节点,故父节点的父节点为黑色节点
1)LL型
插入数据父节点的兄弟节点只会为null,如果不为null则父节点的兄弟节点子树与父节点将不是平衡的。
修改父节点为黑色节点,祖父节点修改为红色节点
对祖父节点右旋转,让父节点为子树根节点,由于子树黑节点数量没有发生变化,故旋转后黑节点数量和旋转前一样
2)LR型
父节点左旋转将变为LL型,按LL型处理
3)RR型
修改父节点为黑色,祖父节点修改为红色,对祖父节点左旋转,让父节点为子树根节点,旋转后和旋转前黑色节点数量没有改变,故事黑色节点平衡
4)RL型
父节点右旋转变为RR型,按RR型处理
示例说明:
插入数据:4,3,6,5,7,8,9,10,11














package com.vincent;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
RBTree<Integer> tree = new RBTree<>();
for(Integer data : Arrays.asList(4,3,6,5,7,8,9,10,11)){
tree.add(data);
System.out.println(tree.getRoot());
}
System.out.println();
List<Integer> list = tree.infixList();
System.out.println(list);
tree.delete(list.get(0));
list = tree.infixList();
System.out.println(list);
tree.delete(list.get(list.size()-1));
list = tree.infixList();
System.out.println(list);
tree.delete(list.get(list.size()/2));
list = tree.infixList();
System.out.println(list);
}
}
//红黑树
class RBTree<T extends Comparable<T>>{
private static final int RED = 0;
private static final int BLACK = 1;
static class Node<T extends Comparable<T>> {
private T item;
private int color = RED;
private Node<T> left;
private Node<T> right;
private Node<T> parent;
public Node(T item) {
this.item = item;
}
public T getItem(){
return item;
}
/**
* 添加节点到当前节点树
* @param node 添加的节点
*/
public void add(Node<T> node){
if(node.item.compareTo(this.item) <= 0){
if(this.left == null){
this.left = node;
node.parent = this;
}
else{
this.left.add(node);
}
}
else{
if(this.right == null){
this.right = node;
node.parent = this;
}
else{
this.right.add(node);
}
}
}
/**
* 通过item查找所在树的结点
* @param item
* @return
*/
public Node<T> searchNode(T item){
int cmp = item.compareTo(this.item);
if(cmp == 0){
return this;
}
Node<T> node = null;
if(cmp < 0 && this.left != null && (node=this.left.searchNode(item)) != null){
return node;
}
else if(cmp > 0 && this.right != null && (node=this.right.searchNode(item)) != null){
return node;
}
return null;
}
/**
* 删除节点,如果删除的节点为根节点将有NullPointerException
*/
public void del(){
Node<T> parent = this.parent;
if(this.left==null && this.right==null){//删除节点为叶子节点
if(parent.left == this){
parent.left = null;
}
else{
parent.right = null;
}
}
else if(this.left == null){//删除节点只有右子树
if(parent.left == this){
parent.left = this.right;
}
else{
parent.right = this.right;
}
}
else if(this.right == null){//删除节点只有左子树
if(parent.left == this){
parent.left = this.left;
}
else{
parent.right = this.left;
}
}
else{//删除节点有左右节点,从删除节点左子树中寻找最大值
Node<T> maxNode = this.left;
while(maxNode.right != null){
maxNode = maxNode.right;
}
if(maxNode == this.left){//删除节点左子树的最大值就是删除节点的左节点
this.item = maxNode.item;
this.left = maxNode.left;
}
else{
maxNode.parent.right = null;
this.item = maxNode.item;
}
}
}
/**
* 节点的中序遍历
* @param dst 遍历节点的存放容器
*/
public void infixList(List<T> dst){
if(this.left != null){
this.left.infixList(dst);
}
dst.add(this.item);
if(this.right != null){
this.right.infixList(dst);
}
}
/**
* 该节点树的最大高度
* @return
*/
public int height(){
return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
}
/**
* 对当前节点左旋转操作
* @return 返回左旋转操作后子树根节点(即当前节点的右节点)
*/
public Node<T> leftRotate(){
if(this.right==null){
return null;
}
Node<T> right = this.right;
this.right = right.left;
if(right.left != null) {
right.left.parent = this;
}
right.left = this;
right.parent = this.parent;
if(this.parent != null) {
if (this.parent.left == this) {
this.parent.left = right;
} else {
this.parent.right = right;
}
}
this.parent = right;
return right;
}
/**
* 对当前节点右旋转操作
* @return 返回右旋转操作后子树根节点(即当前节点的左节点)
*/
public Node<T> rightRotate(){
if(this.left==null){
return null;
}
Node<T> left = this.left;
this.left = left.right;
if(left.right != null) {
left.right.parent = this;
}
left.right = this;
left.parent = this.parent;
if(this.parent != null) {
if (this.parent.left == this) {
this.parent.left = left;
} else {
this.parent.right = left;
}
}
this.parent = left;
return left;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
}
private Node<T> root;
Node<T> getRoot(){
return root;
}
public void add(T item){
Node<T> node = new Node<>(item);
if(this.root == null){
this.root = node;
}
else{
Node<T> old = this.root.searchNode(item);
if(old != null){
old.item = item;
}
else{
this.root.add(node);
}
}
rebalance(node);
}
/**
* 调整树的平衡性,会依次回溯到父节点
* @param node 当前插入的节点
*/
private void rebalance(Node<T> node){
Node<T> parent = node.parent;
if(parent == null){//树的根节点
node.color = BLACK;
return;
}
else if(parent.color == RED) {//红节点一定有父节点
Node<T> pparent = parent.parent;
//父节点的兄弟节点
Node<T> psibling = pparent.left == parent ? pparent.right : pparent.left;
if (psibling != null && psibling.color == RED) {
parent.color = psibling.color = BLACK;
pparent.color = RED;
rebalance(pparent);
}
else if (psibling == null || psibling.color == BLACK) {
Node<T> subRoot = null;
if (pparent.left == parent && parent.left == node) {//LL型
parent.color = BLACK;
pparent.color = RED;
subRoot = pparent.rightRotate();
} else if (pparent.left == parent && parent.right == node) {//LR型
parent.leftRotate();
node.color = BLACK;
pparent.color = RED;
subRoot = pparent.rightRotate();
} else if (pparent.right == parent && parent.left == node) {//RL型
parent.rightRotate();
node.color = BLACK;
pparent.color = RED;
subRoot = pparent.leftRotate();
} else {//RR型
parent.color = BLACK;
pparent.color = RED;
subRoot = pparent.leftRotate();
}
//设置树的根节点
if (subRoot.parent == null) {
this.root = subRoot;
}
}
}
}
public void delete(T item){
Node<T> delNode = null;
if(root.item.compareTo(item)==0){//删除根节点
delNode = root;
if(root.left==null && root.right==null){
this.root = null;
}
else if(root.left==null){
this.root = root.right;
}
else if(root.right==null){
this.root = root.left;
}
else{//根节点有左右节点,扫描右子树最小节点
Node<T> node = this.root.right;
while(node.left!=null){
node = node.left;
}
if(node == this.root.right){
this.root.item = node.item;
this.root.right = node.right;
}
else {
node.parent.left = null;
this.root.item = node.item;
}
}
}
else {
Node<T> node = this.root.searchNode(item);
delNode = node;
if (node != null) {
node.del();
}
}
if(delNode != null){
this.rebalance(delNode);
}
}
public List<T> infixList(){
List<T> rst = new ArrayList<>();
this.root.infixList(rst);
return rst;
}
public int height(){
return this.root.height();
}
}
三.总结
红黑树的平衡处理相对AVL较为宽松,数据插入/删除时效率相对较高
红黑树是一种非严格平衡的二叉搜索树,它的每个节点都有红色或黑色,并遵循特定规则保持平衡。在插入和删除操作中,通过旋转和调整节点颜色保持黑节点平衡。本文介绍了红黑树的基本概念、插入节点的处理策略及其优势。

634

被折叠的 条评论
为什么被折叠?



