红黑树

377 阅读5分钟

红黑树的性质

  1. 每个节点要么是红的,要么是黑的。
  2. 根节点是黑的。
  3. 叶结点nil是黑的。
  4. 如果一个节点是红的,则他的两个子节点都是黑的。
  5. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

红黑树的实现

/**
 * 红黑树
 */
public class RedBlackTree {

    // 表示所有空节点的常量,不存储数据,默认为黑色
    public static final Node nil = new Node(0, false);

    // 初始条件下,根节点为nil
    private Node root = nil;


    // 最大值
    public int max() {
        return max(root).data;
    }

    // 最小值
    public int min() {
        return min(root).data;
    }

    // 中序遍历
    public void inOrderTraverse1() {
        inOrderTraverse(root);
    }

    // 插入操作
    public void insert(int data) {
        // 待插入节点
        Node z = new Node(data, true);
        // 查找插入位置
        // y为x父节点,x用来遍历。
        // 默认y为nil,x为根节点。
        Node y = nil;
        Node x = root;
        while (x != nil) {
            y = x;
            if (data < x.data) {
                x = x.left;
            } else {
                x = x.right;
            }
        }
        // 正式插入元素,调整指针
        z.parent = y;
        // 只有根节点的父节点才会为nil
        // 根节点不为空,通过待插入元素比父节点大还是小比较,
        if (y == nil) {
            root = z;
        } else if (data < y.data){
            y.left = z;
        } else {
            y.right = z;
        }
        // 插入节点的左右孩子默认为黑色节点nil,
        // 他的颜色默认为红色,
        // 默认为红色不会导致黑高发生变化,
        // 但是可能会因父节点为红色,
        // 导致有连续的父子为红节点,
        // 违背红黑树基本性质
        // 这条性质的目的是:防止节点数量为n的红黑树的高度超过2log(n+1),
        // 影响红黑树的平衡,从而使查询的时间复杂度的不稳定
        z.left = nil;
        z.right = nil;
        z.red = true;

        // 对父子连续为红节点的情况,进行处理(插入修正)
        // z为插入节点
        insert_fixup(z);

    }

    // 删除操作
    public void remove(int data) {
        Node z = search(data);
        Node y = z;
        Node x = nil;
        boolean yOrignalRed = y.red;
        if (z.left == nil) {
            x = z.right;
            transplant(z, z.right);
        } else if (z.right == nil) {
            x = z.left;
            transplant(z, z.left);
        } else {
            y = min(z.right);
            yOrignalRed = y.red;
            x = y.right;
            if (y.parent == z) {
                x.parent = y;
            } else {
                transplant(y, y.right);
                y.right = z.right;
                y.right.parent = y;
            }
            transplant(z, y);
            y.left = z.left;
            y.left.parent = y;
            y.red = z.red;
        }
        if (!yOrignalRed) {
            remove_fixup(x);
        }
    }

    // 查询
    public Node search(int data) {
        if (root != null) {
            Node p = root;
            while (true) {
                if (data < p.data) {
                    if (p.left != null) {
                        p = p.left;
                    }else{
                        break;
                    }
                }
                if (data > p.data) {
                    if (p.left != null) {
                        p = p.right;
                    }else{
                        break;
                    }
                }
                if (data == p.data) {
                    return p;
                }
            }
        }
        return null;
    }

    // 插入修正
    private void insert_fixup(Node z) {
        // 这个循环每次都会改变一个节点为红色,
        // 而可能他的父节点也为红色
        // 这是一个无限向根节点接近的过程
        // 所以可能将根节点设置为红色
        // 这违背了红黑树的性质:根节点为黑色。
        // 所以循环结束,要把根节点颜色设置为黑色
        while (z.parent.red) {
            // 插入节点的父节点为爷爷节点的左孩子
            if (z.parent == z.parent.parent.left) {
                // 取得插入节点的爷爷节点的右孩子,即插入元素的叔伯节点
                Node y = z.parent.parent.right;
                // 叔伯节点为红色
                if (y.red) {
                    // 父节点和叔伯节点变黑,意味着黑高增加1,
                    // 对于爷爷节点所在的子树而言,
                    // 他的兄弟节点和他自己黑高不相等,
                    // 会使得爷爷节点的父节点的左右孩子到达叶子结点的路径上黑节点数量不相等
                    // 所以不仅父节点和叔伯节点要变色,爷爷节点也要变色
                    // 父节点变黑
                    z.parent.red = false;
                    // 叔伯节点变黑
                    y.red = false;
                    // 爷爷节点变红
                    z.parent.parent.red = true;
                    // 变红的爷爷节点可能会影响性质
                    z = z.parent.parent;
                }else if (z == z.parent.right) { // 叔伯节点为黑色,插入节点为父节点的右孩子
                    // 父节点为爷爷节点的左孩子,自己(插入节点)为父节点的右孩子,要以父节点为中心左旋,
                    // 让爷爷节点,父节点,自己节点线性化(变成父为爷爷的左孩子,自己为父的左孩子)
                    // 更新z为其父节点是因为左旋后,z会和父节点交换位置
                    z = z.parent;
                    left_rotate(z);
                }
                // 父为爷爷的左孩子,自己为父的左孩子,以爷爷为中心,
                // 右旋操作会把黑色爷爷节点转移到爷爷的右孩子的位置,
                // 使爷爷右孩子黑高增加1,
                // 还从爷爷开始到叶子节点的左右两条路径的黑色节点长度不相等,违背了红黑树基本性质
                // (该性质也是为了维护红黑树的平衡,间接使得查询操作的时间复杂度更稳定)
                // 所以要先将父节点变黑,爷爷节点变红,再进行旋转
                z.parent.red = false;
                z.parent.parent.red = true;
                right_rotate(z.parent.parent);

            } else { // 父节点为爷爷节点的右孩子 处理方式和上面差不多,只不过爷爷节点左右孩子对调
                // 取得插入节点的爷爷节点的左孩子,即插入元素的叔伯节点
                Node y = z.parent.parent.left;
                if (y.red) {
                    z.parent.red = false;
                    y.red = false;
                    z.parent.parent.red = true;
                    z = z.parent.parent;
                 }else if (z == z.parent.left) {
                    z = z.parent;
                    right_rotate(z);
                }
                z.parent.red = false;
                z.parent.parent.red = true;
                left_rotate(z.parent.parent);
            }
        }
        root.red = false;
    }

    // 左旋操作
    // 就是将当前节点的右孩子提升到自己的位置,
    // 自己作为其左孩子存在,自己的右子树变成其左子树
    private void left_rotate(Node x) {
        // 记录要取代自己位置的右孩子
        Node y = x.right;
        // 更新自己的右孩子为右孩子的左孩子
        x.right = y.left;
        if (y.left != nil) {
            y.left.parent = x;
        }
        // 更新右孩子的父指针为自己的父亲,
        // 通过判断当前节点的父节点是否为nil来判断是否为根节点,然后直接更新根节点
        // 判断当前节点是父节点的左孩子还是右孩子,
        // 然后更新父节点的左或右孩子指针
        y.parent = x.parent;
        if (x.parent == nil) {
            root = y;
        }else if (x == x.parent.left) {
            x.parent.left = y;
        }else {
            x.parent.right = y;
        }
        // 最后设置原来的根节点为原来右孩子的左孩子
        // 更新父指针和子指针。
        y.left = x;
        x.parent = y;
    }

    // 右旋操作
    private void right_rotate(Node x) {
        // 记录要取代自己位置的左孩子
        Node y = x.left;
        // 更新自己的左孩子为左孩子的右孩子
        x.left = y.right;
        if (y.right != nil) {
            y.right.parent = x;
        }
        // 更新右孩子的父指针为自己的父亲,
        // 通过判断当前节点的父节点是否为nil来判断是否为根节点,然后直接更新根节点
        // 判断当前节点是父节点的左孩子还是右孩子,
        // 然后更新父节点的左或右孩子指针
        y.parent = x.parent;
        if (x.parent == nil) {
            root = y;
        }else if (x == x.parent.left) {
            x.parent.left = y;
        }else {
            x.parent.right = y;
        }
        // 最后设置原来的根节点为原来左孩子的右孩子
        // 更新父指针和子指针。
        y.right = x;
        x.parent = y;
    }

    // 删除修正
    private void remove_fixup(Node x) {
        while (x != root && !x.red) {
            if (x == x.parent.left) {
                Node w = x.parent.right;
                if (w.red) {
                    w.red = false;
                    x.parent.red = true;
                    left_rotate(x.parent);
                    w = x.parent.right;
                }
                if (!w.left.red && !w.right.red) {
                    w.red = true;
                    x = x.parent;
                } else if (!w.right.red) {
                    w.left.red = false;
                    w.red = true;
                    right_rotate(w);
                    w = x.parent.right;
                }
                w.red = x.parent.red;
                x.parent.red = false;
                w.right.red = false;
                left_rotate(x.parent);
                x = root;
            } else {
                Node w = x.parent.left;
                if (w.red) {
                    w.red = false;
                    x.parent.red = true;
                    right_rotate(x.parent);
                    w = x.parent.left;
                }
                if (!w.left.red && !w.right.red){
                    w.red = true;
                    x = x.parent;
                } else if (!w.right.red) {
                    w.left.red = false;
                    w.red = true;
                    left_rotate(w);
                    w = x.parent.right;
                }
                w.red = x.parent.red;
                x.parent.red = false;
                w.right.red = false;
                right_rotate(x.parent);
                x = root;
            }
        }
        x.red = false;
    }

    // 用v替换u指针
    private void transplant(Node u, Node v) {
        if (u.parent == nil) {
            root = v;
        } else if (u == u.parent.left) {
            u.parent.left = v;
        } else {
            u.parent.right = v;
        }
        v.parent = u.parent;
    }

    // 最大值
    private Node max(Node root) {
        if (root != nil) {
            Node p = root;
            while (p.right != nil) {
                p = p.right;
            }
            return p;
        }
        return nil;
    }

    // 最小值
    private Node min(Node root) {
        if (root != nil) {
            Node p = root;
            while (p.left != nil) {
                p = p.left;
            }
            return p;
        }
        return nil;
    }

    // 中序遍历
    private void inOrderTraverse(Node root) {
        if (root != nil) {
            inOrderTraverse(root.left);
            System.out.println(root.data);
            inOrderTraverse(root.right);
        }
    }

    private static class Node {
        Node parent, left, right;
        int data;
        boolean red;

        public Node() {
            this.red = true;
            parent = left = right = nil;
        }
        public Node(int data, boolean red) {
            this();
            this.data = data;
            this.red = red;
        }
    }
}