红黑树详解

1.概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树详解_第1张图片

2.性质
最长路径最多是最短路径的2倍

每个结点不是红色就是黑色

根节点是黑色的

如果一个结点是红色的,则它的俩个孩子结点是黑色的【没有俩个连续的红色结点】

对于每个结点,从该节点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

3.实现红黑树

新增的结点不能是黑色的,因为如果是黑色的,那么就需要保证每条路径上的黑色结点必须是相同的。弄不好还得新增加一堆黑色结点。

所以我们新增的结点默认是红色的。

package test;


enum COLOR {
    RED, BLACK
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode parent;
    COLOR color;

    public TreeNode(int val) {
        this.val = val;
        this.color = COLOR.RED;
    }
}

public class RBTree {
    public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            root.color = COLOR.BLACK;
            return true;
        }

        TreeNode cur = root;
        TreeNode pre = null;

        while (cur != null) {
            if (cur.val < val) {
                pre = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                pre = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }

        if (pre.val < val) {
            pre.right = node;
        } else {
            pre.left = node;
        }
        node.parent = pre;
        cur = node;

        while (pre != null && pre.color == COLOR.RED) {
            TreeNode grandFather = pre.parent;
            if (pre == grandFather.left) {
                TreeNode uncle = grandFather.right;
                if (uncle != null && uncle.color == COLOR.RED) {
                    pre.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandFather.color = COLOR.RED;
                    cur = grandFather;
                    pre = cur.parent;
                } else {
                    //uncle不存在 或者uncle是黑色的
                    //情况三
                    if (cur == pre.right) {
                        rotateLeft(pre);
                        TreeNode tmp = pre;
                        pre = cur;
                        cur = tmp;
                    }//


                    //情况二
                    rotateRight(grandFather);
                    grandFather.color = COLOR.RED;
                    pre.color = COLOR.BLACK;
                }
            } else {
                //pre == grandFather.left
                TreeNode uncle = grandFather.left;
                if (uncle != null && uncle.color == COLOR.RED) {
                    pre.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandFather.color = COLOR.RED;
                    cur = grandFather;
                    pre = cur.parent;
                } else {
                    if (cur == pre.left) {
                        rotateRight(pre);
                        TreeNode tmp = pre;
                        pre = cur;
                        cur = tmp;
                    }
                    rotateLeft(grandFather);
                    grandFather.color = COLOR.RED;
                    pre.color = COLOR.BLACK;
                }
            }
        }

        root.color = COLOR.BLACK;
        return true;
    }

    //左单旋
    public void rotateLeft(TreeNode pre) {
        TreeNode subR = pre.right;
        TreeNode subRL = subR.left;

        pre.right = subRL;
        subR.left = pre;

        if (subRL != null) {
            subRL.parent = pre;
        }

        TreeNode pPre = pre.parent;
        pre.parent = subR;

        if (root == pre) {
            root = subR;
            root.parent = null;
        } else {
            if (pPre.left == pre) {
                pPre.left = subR;
            } else {
                pPre.right = subR;
            }
            subR.parent = pPre;
        }
    }

    //右单旋
    public void rotateRight(TreeNode pre) {
        TreeNode subL = pre.left;
        TreeNode subLR = subL.right;
        pre.left = subLR;
        subL.right = pre;
        //没有subLR的时候
        if (subLR != null) {
            subLR.parent = pre;
        }
        TreeNode pPre = pre.parent;
        pre.parent = subL;
        //检查当前是不是根节点
        if (pre == root) {
            root = subL;
            root.parent = null;
        } else {
            //不是根节点,判断这棵树是左子树还是右子树
            if (pPre.left == pre) {
                pPre.left = subL;
            } else {
                pPre.right = subL;
            }
            subL.parent = pPre;
        }
    }

    //判断当前树是不是红黑树
    public boolean isRBTree() {
        if (root == null) {
            //如果一棵树是空树,那么这棵树就是红黑树
            return true;
        }
        if (root.color != COLOR.BLACK) {
            System.out.println("违反了性质,根节点必须是黑色的!");
        }

        //最左边路径黑色结点的个数
        int blackNum = 0;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.color == COLOR.BLACK) {
                blackNum++;
            }
            cur = cur.left;
        }

        //检查是否存在俩个连续的红色结点
        return checkRedColor(root) && checkBlackNum(root, 0, blackNum);
    }

    /**
     * @param root
     * @param pathBlackNum 每次递归的时候计算黑色结点的个数
     * @param blackNum     实现计算好的某条路径上的黑色结点个数
     * @return
     */
    public boolean checkBlackNum(TreeNode root, int pathBlackNum, int blackNum) {
        if (root == null) {
            return true;
        }
        if (root.color == COLOR.BLACK) {
            pathBlackNum++;
        }
        if (root.left == null && root.right == null) {
            if (pathBlackNum != blackNum) {
                System.out.println("违反了性质:每条路径上黑色结点个数是不一样的");
                return false;
            }
        }
        return checkBlackNum(root.left, pathBlackNum, blackNum) && checkBlackNum(root.right, pathBlackNum, blackNum);

    }

    public boolean checkRedColor(TreeNode root) {
        if (root == null) {
            return true;
        }
        if (root.color == COLOR.RED) {
            TreeNode pre = root.parent;
            if (pre.color == COLOR.RED) {
                System.out.println("违反了性质:连续俩个红色的结点连在一起");
                return false;
            }
        }
        return checkRedColor(root.left) && checkRedColor(root.right);
    }

    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        System.out.print(" " + root.val + " ");
        inorder(root.right);
    }

    public static void main(String[] args) {
        int[] array = {4, 2, 6, 1, 3, 5, 15, 7, 16, 14};
        RBTree rbTree = new RBTree();
        for (int i = 0; i < array.length; i++) {
            rbTree.insert(array[i]);
        }
        rbTree.inorder(rbTree.root);
        System.out.println();
        System.out.println(rbTree.isRBTree());
    }
}

4.AVL树和红黑树比较 

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2^N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

5.红黑树应用

java集合框架中的:TreeMap、TreeSet底层使用的就是红黑树
 

未完待续~

你可能感兴趣的:(数据结构,算法,数据结构)