红黑树

一)红黑树简介

规则1、 每个节点要么是黑色,要么是红色(也可以用其它颜色表示,红黑色比较常用)。

规则2、根节点一直是黑色的。

规则3、每个叶子节点都是黑色的,并且为null(每个叶子到根的所有路径上不能有两个连续的红色节点)。

规则4、从根节点到叶子节点或null子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

规则5、新插入的节点颜色总是红色的,因为黑色比红色多,插入红色违背上面4点可能性小。

 

优点:新增和删除相对于AVL平衡二叉树耗费性能小。

缺点:在极端的情况下,比AVL平衡二叉树查询慢。 

备注:红黑树适用于新增和删除多的情况,AVL平衡二叉树适用于查询多的情况。 

 

原图模型:(如果节点下面没有子节点,会默认为NULL黑色节点,也称为“黑色哨兵”)

 

二)红黑树

第一步:定义红黑树结构

/**
 * 红黑树
 * @author ouyangjun
 */
public class RedBlackTree<T extends Comparable<T>> {
	
    private static final boolean RED   = false; // 红色
    private static final boolean BLACK = true;  // 黑色
	
    // 红黑树结构
    static class Node<T> {
        Node<T> parent; // 父节点
        Node<T> left;   // 左节点
        Node<T> right;  // 右节点
        T value;        // 节点的值
        boolean color;  // 节点的颜色, 红色=false、黑色=true
		
        Node(Node<T> parent, Node<T> left, Node<T> right,
				T value, boolean color) {
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.value = value;
            this.color = color;
        }
    }
	
    private Node<T> root; // 根节点
	
    // 获取红黑树节点信息
    public Node<T> getRoot() {
        return root;
    }
}

 

第二步:左旋转

// 左旋转
private Node<T> left(Node<T> x) {
		
    Node<T> y = x.right; // 记录节点X的右节点Y
    x.right = y.left; // 将节点Y的左节点LY作为节点X的右节点,为空就直接为空了
    if (y.left != null) { // 判断节点Y是否有左节点LY
        y.left.parent = x; // 把节点X作为节点LY的父节点
    }
		
    y.parent = x.parent; // 将节点X的父节点作为节点Y的父节点
    if (x.parent == null) { // 判断节点X是否有父节点
        // 没有父节点,直接把节点Y作为根节点
        root = y;
    } else {
        if (x == x.parent.left) { // 判断节点X属于节点P的左节点还是右节点
            x.parent.left = y; // 将节点Y作为节点X父节点下的左节点
        } else {
            x.parent.right = y; // 将节点Y作为节点X父节点下的右节点
        }
    }
		
    y.left = x; // 把节点X作为节点Y的左节点
    x.parent = y; // 把节点Y作为节点X的父节点
		
    // 返回
    return y;
}

 

第三步:右旋转

// 右旋转
private Node<T> right(Node<T> y) {
		
    Node<T> x = y.left; // 记录节点Y的左节点X
    y.left = x.right; // 将节点X的右节点RX作为节点Y的右节点,为空就直接为空了
    if (x.right != null) { // 判断节点X是否有右节点RX
        x.right.parent = y; // 把节点Y作为节点RX的父节点
    }
		
    x.parent = y.parent; // 将节点Y的父节点作为节点X的父节点
    if (x.parent == null) { // 判断节点Y是否有父节点
        // 没有父节点,直接把节点X作为根节点
        root = x;
    } else {
        if (y == y.parent.left) { // 判断节点Y属于节点P的左节点还是右节点
            y.parent.left = x; // 将节点X作为节点Y父节点下的左节点
        } else {
            y.parent.right = x; // 将节点X作为节点Y父节点下的右节点
        }
    }
		
    x.right = y; // 把节点Y作为节点X的左节点
    y.parent = x; // 把节点X作为节点Y的父节点
		
    // 返回
    return x;
}

 

第四步:新增节点场景及源码

场景一:黑父,父节点为黑的情况,直接插入新节点,不用做任何变化。

 

场景二:红父红叔,把父节点和叔节点的颜色变黑,如果祖节点是根节点,那就保持黑色(可以先变红再变黑),否则就变红色。

 

场景三:红父黑叔情况一

 

场景四:红父黑叔情况二

 

场景五:红父黑叔情况三

 

场景六:红父黑叔情况四

 

// 新增节点
public Node<T> add(T value) {
    // 新节点
    Node<T> newNode = new Node<T>(null, null, null, value, RED);
		
    // 返回
    return add(newNode);
}
	
private Node<T> add(Node<T> node) {
    if (node == null) {
        return null;
    }
    Node<T> current = null; // 表示最后node的父节点
    Node<T> next = root; // 根节点
		
    // 先查找到需要插入的位置
    while (next != null) {
        current = next;
        int num = node.value.compareTo(next.value);
        if (num < 0) {
            next = next.left; // 在左边新增节点
        } else if(num > 0) {
            next = next.right; // 在右边新增节点
        } else {
            current = null; // 新增的节点重复了,直接返回
            return null;
        }
    }
    node.parent = current; // 找到插入的位置,将current作为node的父节点
		
    // 判断node是左节点还是右节点
    if (current != null){
        int cmp = node.value.compareTo(current.value);
        if (cmp < 0) {
            current.left = node;
        } else {
            current.right = node;
        }
    } else {
        root = node;
    }
	     
    // 新增参数之后, 二叉树可能违反规则, 需要旋转将其修正为一颗红黑树
    return insertFixUp(node);
}

private Node<T> insertFixUp(Node<T> node) {
    if (node == null) {
        return root;
    }
    Node<T> parent, gparent; // 定义父节点和祖父节点
	     
    // 需要修正的条件: 父节点存在,且父节点的颜色是红色
    parent = node.parent;
    while (parent != null && !parent.color) {
        gparent = parent.parent; // 获得祖父节点
	         
        // 若父节点是祖父节点的左子节点,下面的else相反
        if (parent == gparent.left) {
            Node<T> uncle = gparent.right; // 获得叔叔节点
	             
            // case1: 叔叔节点也是红色
            if (uncle != null && !uncle.color) {
                uncle.color = BLACK; // 把父节点和叔叔节点涂黑
                parent.color = BLACK;
                gparent.color = RED; // 把祖父节点涂红
                node = gparent; // 把位置放到祖父节点处
                continue; // 继续while循环,重新判断
            }
	             
            // case2: 叔叔节点是黑色,且当前节点是右子节点
            if (node == parent.right) {
                left(parent); // 从父节点出左旋
                Node<T> tmp = parent; // 然后将父节点和自己调换一下,为下面右旋做准备
                parent = node;
                node = tmp;
            }
	             
            // case3: 叔叔节点是黑色,且当前节点是左子节点
            parent.color = BLACK;
            gparent.color = RED;
            right(gparent);
        } else { // 若父节点是祖父节点的右子节点,与上面的情况完全相反,本质是一样的
            Node<T> uncle = gparent.left;
	             
            // case1: 叔叔节点也是红色的
            if (uncle != null && !uncle.color) {
                uncle.color = BLACK; // 把父节点和叔叔节点涂黑
                parent.color = BLACK;
                gparent.color = RED; // 把祖父节点涂红
                node = gparent; // 把位置放到祖父节点处
                continue; // 继续while循环,重新判断
            }
	             
            // case2: 叔叔节点是黑色的,且当前节点是左子节点
            if (node == parent.left) {
                right(parent);
                Node<T> tmp = parent;
                parent = node;
                node = tmp;
            }
	             
            // case3: 叔叔节点是黑色的,且当前节点是右子节点
            parent.color = BLACK;
            gparent.color = RED;
            left(gparent);
        }
    }

    //将根节点设置为黑色,保持根节点一直是黑色
    Node<T> rootNode = root;
    rootNode.color = BLACK;
	    
    // 返回
    return rootNode;
}

 

第五步:增加几个辅助方法

// 查询节点
private Node<T> search(T value, Node<T> node){
    if (node != null) {
        int num = value.compareTo(node.value);
        if (num < 0) {
            return search(value, node.left);
        } else if(num > 0) {
            return search(value, node.right);
        } else {
            return node;
        }
    }
    return null;
}
	
// 寻找后继节点,即大于该节点的最小节点
private Node<T> min(Node<T> node) {
    if (node == null) {
        return null;
    }
		
    if (node.left == null) {
        return node;
    }
		
    while (node.left != null) {
        node = node.left;
    }
    return node;
}

 

第六步:删除节点

// 删除节点
public Node<T> remove(T value) {
    // 删除节点
    Node<T> removeNode = search(value, root);
    // 判断是否找到值
    if (removeNode != null) {
        return remove(removeNode);
    }
    // 返回
    return null;
}
	
private Node<T> remove(Node<T> node) {
    Node<T> child, parent, replace;
    boolean color = true;
		
    if (node.left != null && node.right != null) {
        replace = min(node.right);
        if (replace != null) {
            parent = replace.parent;
        } else {
            parent = null;
        }
		   
        child = replace.right;
        color = replace.color;
        if (node == parent) {
            parent = replace;
        } else {
            if (child != null) {
                child.parent = parent;
            }
            replace.parent.left = child;
            replace.right = node.right;
            if (node.right != null) {
                node.right.parent = replace; 
            }
        }
		   
        replace.parent = node.parent;
        replace.left = node.left;
        if (node.left != null) {
            node.left.parent = replace;
        }
        replace.color = node.color;
		   
        if (node.parent != null) {
            if(node.parent.left==node) {
                node.parent.left = replace;
            } else {
                node.parent.right = replace;
            }
        } else {
            root = replace;
        }
		   
        if (color) {
            removeFixUp(child, parent);
        }
    } else {
			
        if (node.left != null) {
            replace = node.left;
        } else {
            replace = node.right;
        }
			
        parent = node.parent;
			
        if (parent != null) {
            if (parent.left == node) {
                parent.left = replace;
            } else {
                parent.right = replace;
            }
        } else {
            root = replace;
        }
			
        if (replace !=null) {
            replace.parent = parent;
        }
        color = node.color;
        child = replace;
			
        if (color) {
            removeFixUp(child, parent);
        }
    }
		
    return root;
}
	
private void removeFixUp(Node<T> node, Node<T> parentNode) {
    Node<T> other;
		
    // node不为空且为黑色,并且不为根节点
    while ((null == node || node.color) && (node != this.root)) {
        // node是父节点的左孩子
        if (node == parentNode.left) {
            // 获取到其右孩子
            other = parentNode.right;
            // node节点的兄弟节点是红色
            if (!other.color) {
                other.color = BLACK;
                parentNode.color = RED;
                left(parentNode);
                other = parentNode.right;
            }

            // node节点的兄弟节点是黑色,且兄弟节点的两个孩子节点也是黑色
            if ((other.left == null || other.left.color) &&
	                    (other.right == null || other.right.color)) {
                other.color = RED;
                node = parentNode;
                parentNode = node.parent;
            } else {
                // node节点的兄弟节点是黑色,且兄弟节点的右孩子是红色
                if (null == other.right || other.right.color) {
                    other.left.color = BLACK;
                    other.color = RED;
                    right(other);
                    other = parentNode.right;
                }
                // node节点的兄弟节点是黑色,且兄弟节点的右孩子是红色,左孩子是任意颜色
                other.color = parentNode.color;
	                
                parentNode.color = BLACK;
                other.right.color = BLACK;
                left(parentNode);
                node = this.root;
                break;
            }
        } else {
            other = parentNode.left;
            if (!other.color) {
                other.color = BLACK;
                parentNode.color = RED;
                right(parentNode);
                other = parentNode.left;
            }

            if ((null == other.left || other.left.color) &&
	                    (null == other.right || other.right.color)) {
                other.color = RED;
                node = parentNode;
                parentNode = node.parent;
            } else {
                if (null == other.left || other.left.color) {
                    other.right.color = BLACK;
                    other.color = RED;
                    left(other);
                    other = parentNode.left;
                }

                other.color = parentNode.color;
                parentNode.color = BLACK;
                other.left.color = BLACK;
                right(parentNode);
                node = this.root;
                break;
            }
        }
    }
    if (node != null) {
        node.color = BLACK;
    }
}

 

第七步:遍历方式

// 转换颜色
private String getColor(boolean color) {
    return color ? "黑" : "红";
}
	
/**
 * 前序遍历: 根节点 ==> 左节点 ==> 右节点
 * @param node
 */
public void preNode(Node<T> node) {
    if (node != null) {
        System.out.print(node.value + "(" + getColor(node.color) + ")\t");
			
        preNode(node.left);
			
        preNode(node.right);
    }
}
	
/**
 * 中序遍历: 左节点 ==> 根节点 ==> 右节点
 * @param node
 */
public void inNode(Node<T> node) {
    if (node != null) {
        inNode(node.left);
			
        System.out.print(node.value + "(" + getColor(node.color) + ")\t");
			
        inNode(node.right);
    }
}
	
/**
 * 后序遍历: 左节点 ==> 右节点 ==> 根节点
 * @param node
 */
public void nextNode(Node<T> node) {
    if (node != null) {
        nextNode(node.left);
			
        nextNode(node.right);
        
        System.out.print(node.value + "(" + getColor(node.color) + ")\t");
    }
}

 

第八步:main方法测试源码

public static void main(String[] args) {
    // 初始化
    RedBlackTree<Integer> redblack = new RedBlackTree<Integer>();
		
    // --------------------新增节点演示begin--------------------
    redblack.add(29); // 添加根节点29
    redblack.add(21); // 在节点29下添加左节点21
    redblack.add(28); // 在节点21下添加左节点28
    redblack.add(46); // 在节点29下添加右节点46
    redblack.add(10); // 在节点21下面添加左节点10
    redblack.add(13); // 在节点10下面添加左节点13
    redblack.add(59); // 在节点46下添加右节点59
    redblack.add(51); // 在节点59下添加左节点51
    redblack.add(38); // 在节点46下添加右节点38
    redblack.add(33); // 在节点38下添加右节点33
    // --------------------新增节点演示end--------------------
		
    System.out.println("前序遍历结果为:");
    redblack.preNode(redblack.getRoot());
		
    System.out.println("\n中序遍历结果为:");
    redblack.inNode(redblack.getRoot());
		
    System.out.println("\n后序遍历结果为:");
    redblack.nextNode(redblack.getRoot());
}

打印效果图:

 

识别二维码关注个人微信公众号

本章完结,待续,欢迎转载!
 
本文说明:该文章属于原创,如需转载,请标明文章转载来源!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值