数据结构篇08、红黑树

与AVL树类似,红黑树也是一种平衡的二分搜索树,但是对平衡的定义不一样,红黑树每个节点都有颜色值,非红即黑,平衡性也是指黑平衡;我们实现的这个红黑树是一棵左倾红黑树,可通过2-3树进行类比;

首先列一下红黑树的5条性质:

  1. 每个节点要么是红节点,要么是黑节点;
  2. 根节点一定是黑节点;
  3. 叶子节点一定是黑节点,此处的叶子节点指空节点;
  4. 红色节点的孩子节点一定是黑色节点;
  5. 任一节点到叶子节点所经过的黑色节点的个数是相等的;

从第五条性质可以看出红黑树是一棵黑平衡的树;

1、Node节点及红黑树基本属性

为了方便使用红黑树实现映射map,因此泛型设置为K和V,这点与AVL是相同的;java标准库中的TreeMap的底层实现就是红黑树;

Node节点类包括key、value、左右孩子节点left和right、还有一个color属性用于维护节点的颜色,最终保持黑平衡;我们在AVL中是用height属性来维护的平衡;

root属性表示红黑树的根节点;size表示红黑树中元素个数;

getSize返回红黑树中元素个数;isEmpty返回红黑树是否为空;

isRed判断节点是否为红节点,红节点返回true,黑节点返回false;

public class RBTree<K extends Comparable<K>, V> {

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private class Node{
        public K key;
        public V value;
        public Node left, right;
        public boolean color;

        public Node(K key, V value){
            this.key = key;
            this.value = value;
            left = null;
            right = null;
            color = RED;
        }
    }

    private Node root;
    private int size;

    public RBTree(){
        root = null;
        size = 0;
    }

    public int getSize(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    // 判断节点node的颜色
    private boolean isRed(Node node){
        if(node == null)
            return BLACK;
        return node.color;
    }

    、、、
}
复制代码

2、左旋转、右旋转与颜色翻转

左旋转、右旋转和颜色翻转是三个子过程,用于维护在添加元素时保持红黑树的黑平衡;

//   node                     x
//  /   \     左旋转         /  \
// T1   x   --------->   node   T3
//     / \              /   \
//    T2 T3            T1   T2
private Node leftRotate(Node node){

    Node x = node.right;

    // 左旋转
    node.right = x.left;
    x.left = node;

    x.color = node.color;
    node.color = RED;

    return x;
}

//     node                   x
//    /   \     右旋转       /  \
//   x    T2   ------->   y   node
//  / \                       /  \
// y  T1                     T1  T2
private Node rightRotate(Node node){

    Node x = node.left;

    // 右旋转
    node.left = x.right;
    x.right = node;

    x.color = node.color;
    node.color = RED;

    return x;
}

// 颜色翻转
private void flipColors(Node node){

    node.color = RED;
    node.left.color = BLACK;
    node.right.color = BLACK;
}
复制代码

3、向红黑树中添加元素

因为红黑树也是一棵二分搜索树,添加元素的前半部分与二分搜索树相同,通过递归添加到合适的位置;

添加完元素之后需要通过左旋转、右旋转、颜色翻转三个过程来维护一下黑平衡;

// 向红黑树中添加新的元素(key, value)
public void add(K key, V value){
    root = add(root, key, value);
    root.color = BLACK; // 最终根节点为黑色节点
}

// 向以node为根的红黑树中插入元素(key, value),递归算法
// 返回插入新节点后红黑树的根
private Node add(Node node, K key, V value){

    if(node == null){
        size ++;
        return new Node(key, value); // 默认插入红色节点
    }

    if(key.compareTo(node.key) < 0)
        node.left = add(node.left, key, value);
    else if(key.compareTo(node.key) > 0)
        node.right = add(node.right, key, value);
    else // key.compareTo(node.key) == 0
        node.value = value;

    if (isRed(node.right) && !isRed(node.left))
        node = leftRotate(node);

    if (isRed(node.left) && isRed(node.left.left))
        node = rightRotate(node);

    if (isRed(node.left) && isRed(node.right))
        flipColors(node);

    return node;
}
复制代码

4、从红黑树中查找和修改元素

查找与修改元素不涉及节点颜色的维护,因此不需要特殊的过程,因此与数据结构篇05二分搜索树的实现基本一致;

// 返回以node为根节点的二分搜索树中,key所在的节点
private Node getNode(Node node, K key){

    if(node == null)
        return null;

    if(key.equals(node.key))
        return node;
    else if(key.compareTo(node.key) < 0)
        return getNode(node.left, key);
    else // if(key.compareTo(node.key) > 0)
        return getNode(node.right, key);
}

public boolean contains(K key){
    return getNode(root, key) != null;
}

public V get(K key){

    Node node = getNode(root, key);
    return node == null ? null : node.value;
}

public void set(K key, V newValue){
    Node node = getNode(root, key);
    if(node == null)
        throw new IllegalArgumentException(key + " doesn't exist!");

    node.value = newValue;
}
复制代码

5、从红黑树中删除元素

从红黑树中删除元素原理与二分搜索树是一致的,但是删除完元素之后需要维护节点的红黑性质,这部分太过于复杂,我们的删除部分并没有实现删除元素后节点的维护;这里使用的删除方法跟普通的二分搜索树一样;

// 返回以node为根的二分搜索树的最小值所在的节点
private Node minimum(Node node){
    if(node.left == null)
        return node;
    return minimum(node.left);
}

// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
private Node removeMin(Node node){

    if(node.left == null){
        Node rightNode = node.right;
        node.right = null;
        size --;
        return rightNode;
    }

    node.left = removeMin(node.left);
    return node;
}

// 从二分搜索树中删除键为key的节点
public V remove(K key){

    Node node = getNode(root, key);
    if(node != null){
        root = remove(root, key);
        return node.value;
    }
    return null;
}

private Node remove(Node node, K key){

    if( node == null )
        return null;

    if( key.compareTo(node.key) < 0 ){
        node.left = remove(node.left , key);
        return node;
    }
    else if(key.compareTo(node.key) > 0 ){
        node.right = remove(node.right, key);
        return node;
    }
    else{   // key.compareTo(node.key) == 0

        // 待删除节点左子树为空的情况
        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size --;
            return rightNode;
        }

        // 待删除节点右子树为空的情况
        if(node.right == null){
            Node leftNode = node.left;
            node.left = null;
            size --;
            return leftNode;
        }

        // 待删除节点左右子树均不为空的情况

        // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
        // 用这个节点顶替待删除节点的位置
        Node successor = minimum(node.right);
        successor.right = removeMin(node.right);
        successor.left = node.left;

        node.left = node.right = null;

        return successor;
    }
}
复制代码

6、红黑树全部代码

public class RBTree<K extends Comparable<K>, V> {

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private class Node{
        public K key;
        public V value;
        public Node left, right;
        public boolean color;

        public Node(K key, V value){
            this.key = key;
            this.value = value;
            left = null;
            right = null;
            color = RED;
        }
    }

    private Node root;
    private int size;

    public RBTree(){
        root = null;
        size = 0;
    }

    public int getSize(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    // 判断节点node的颜色
    private boolean isRed(Node node){
        if(node == null)
            return BLACK;
        return node.color;
    }

    //   node                     x
    //  /   \     左旋转         /  \
    // T1   x   --------->   node   T3
    //     / \              /   \
    //    T2 T3            T1   T2
    private Node leftRotate(Node node){

        Node x = node.right;

        // 左旋转
        node.right = x.left;
        x.left = node;

        x.color = node.color;
        node.color = RED;

        return x;
    }

    //     node                   x
    //    /   \     右旋转       /  \
    //   x    T2   ------->   y   node
    //  / \                       /  \
    // y  T1                     T1  T2
    private Node rightRotate(Node node){

        Node x = node.left;

        // 右旋转
        node.left = x.right;
        x.right = node;

        x.color = node.color;
        node.color = RED;

        return x;
    }

    // 颜色翻转
    private void flipColors(Node node){

        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

    // 向红黑树中添加新的元素(key, value)
    public void add(K key, V value){
        root = add(root, key, value);
        root.color = BLACK; // 最终根节点为黑色节点
    }

    // 向以node为根的红黑树中插入元素(key, value),递归算法
    // 返回插入新节点后红黑树的根
    private Node add(Node node, K key, V value){

        if(node == null){
            size ++;
            return new Node(key, value); // 默认插入红色节点
        }

        if(key.compareTo(node.key) < 0)
            node.left = add(node.left, key, value);
        else if(key.compareTo(node.key) > 0)
            node.right = add(node.right, key, value);
        else // key.compareTo(node.key) == 0
            node.value = value;

        if (isRed(node.right) && !isRed(node.left))
            node = leftRotate(node);

        if (isRed(node.left) && isRed(node.left.left))
            node = rightRotate(node);

        if (isRed(node.left) && isRed(node.right))
            flipColors(node);

        return node;
    }

    // 返回以node为根节点的二分搜索树中,key所在的节点
    private Node getNode(Node node, K key){

        if(node == null)
            return null;

        if(key.equals(node.key))
            return node;
        else if(key.compareTo(node.key) < 0)
            return getNode(node.left, key);
        else // if(key.compareTo(node.key) > 0)
            return getNode(node.right, key);
    }

    public boolean contains(K key){
        return getNode(root, key) != null;
    }

    public V get(K key){

        Node node = getNode(root, key);
        return node == null ? null : node.value;
    }

    public void set(K key, V newValue){
        Node node = getNode(root, key);
        if(node == null)
            throw new IllegalArgumentException(key + " doesn't exist!");

        node.value = newValue;
    }

    // 返回以node为根的二分搜索树的最小值所在的节点
    private Node minimum(Node node){
        if(node.left == null)
            return node;
        return minimum(node.left);
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMin(Node node){

        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size --;
            return rightNode;
        }

        node.left = removeMin(node.left);
        return node;
    }

    // 从二分搜索树中删除键为key的节点
    public V remove(K key){

        Node node = getNode(root, key);
        if(node != null){
            root = remove(root, key);
            return node.value;
        }
        return null;
    }

    private Node remove(Node node, K key){

        if( node == null )
            return null;

        if( key.compareTo(node.key) < 0 ){
            node.left = remove(node.left , key);
            return node;
        }
        else if(key.compareTo(node.key) > 0 ){
            node.right = remove(node.right, key);
            return node;
        }
        else{   // key.compareTo(node.key) == 0

            // 待删除节点左子树为空的情况
            if(node.left == null){
                Node rightNode = node.right;
                node.right = null;
                size --;
                return rightNode;
            }

            // 待删除节点右子树为空的情况
            if(node.right == null){
                Node leftNode = node.left;
                node.left = null;
                size --;
                return leftNode;
            }

            // 待删除节点左右子树均不为空的情况

            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
            // 用这个节点顶替待删除节点的位置
            Node successor = minimum(node.right);
            successor.right = removeMin(node.right);
            successor.left = node.left;

            node.left = node.right = null;

            return successor;
        }
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享