红黑树

2-3-4树(引出红黑树)

模型

  1. 在普通的二叉查找树上进行扩展,他允许有多个键(1–3个 --> 对应2-node,3-node,4-node)
  2. 树保持完美平衡。(根到每个叶子结点的路径都是一样长的)

特点

动态 添加 和 删除 时能保持完美平衡。

插入

当目标结点是4-node则需要分裂。
分为两种分裂方式:
Bottom-up 自底向上(到目标结点,再一步一步向上分裂)
Top-down 自顶向下(沿着查找路径向下寻找,遇到4-node结点就分裂4-node,确保当前的结点不是4-node,给目标位置新结点的分裂留下空间(使其父结点不是4-node))
这些变换都是局部变换。不影响其他无关的结点。

性能

只与树的高度有关:
在这里插入图片描述

红黑树(2-3-4的树简单实现)

  1. 用BST来表示2-3-4树
  2. 用“内部的”红色边表示3-node和4-node
  3. 2-3-4树能表示成BST,它们之间有一种对应关系,但这种对应关系不是1对1的。2-node和4-node是唯一的,但是3-node不唯一,要考虑的情况就会变多。
    在这里插入图片描述

定义

在这里插入图片描述

  1. 每个结点(子结点)或者是红色的,或者是黑色的
  2. 根结点是黑色的
  3. 叶结点(Nil)是黑色的
  4. 如果一个结点是红色的,则它的两个子结点是黑色的(4-node只有一种编码方式
  5. 对每个结点,从该结点到所有后代叶结点的简单路径上,均包含相同数目的黑色结点。(黑高平衡)

Left-leaning red-black trees(左倾红黑树)

  1. 用BST来表现2-3-4树
  2. 用“内部的”红色边表示3-node和4-node
  3. 3-node的红色边是左倾的

实现

JDK实现普通的红黑树,非LLRB。

局部变换----旋转
package day14;


import java.util.*;

public class RedBlackTree<K extends Comparable<? super K>,V> {
    //颜色常量
    private static final boolean RED = true;
    private static final boolean BLACK = false;
    //属性
    private TreeNode root;

    private class TreeNode {
        K key;
        V value;
        TreeNode left;
        TreeNode right;
        boolean color;
        int size; // 以这个结点为根的树的键值对个数

        public TreeNode(K key, V value, boolean color, int size) {
            this.key = key;
            this.value = value;
            this.color = color;
            this.size = size;
        }
    }
    /***********************************************************
     *                     unordered methods
     **********************************************************/
    /**
     * 插入键值对
     * @param key 键
     * @param value 值
     */
    public void put(K key, V value) {
        if (key == null || value == null) {
            throw new IllegalArgumentException("Key or value cannot be null.");
        }
        root = put(root, key, value);
        root.color = BLACK; // if添加的是第一个结点 或者 分解根结点 都会导致根结点变成红色的,所以要把它变黑。
        check();
    }
    // 对应2-3树
    private TreeNode put(TreeNode x, K key, V value) {
        // 底部添加结点的情形
        if (x == null) return new TreeNode(key, value, RED, 1);
        // 自顶向下分解4-node
       /* if (isRed(x.left) && isRed(x.right)) {  // 如果放在修复阶段,则RBT不会存在4-node
            flipColors(x);
        }*/
        int cmp = key.compareTo(x.key);
        if (cmp < 0) x.left = put(x.left, key, value);
        else if (cmp > 0) x.right = put(x.right, key, value);
        else x.value = value;

        //自底向上修复
        return fixUp(x);
    }
    /**
     * 获取键关联的值
     * @param key 键
     * @return 键关联的值,如果键不存在,返回null
     */
    public V get(K key) {
        if (key == null) {
            throw new IllegalArgumentException("Key cannot be null");
        }
        TreeNode x = root;
        while (x != null) {
            int cmp = key.compareTo(x.key);
            if (cmp < 0) x = x.left;
            else if (cmp > 0) x = x.right;
            else return x.value;
        }
        return null;
    }

    /**
     * 判断键是否存在
     * @param key 键
     * @return 如果键存在返回true,否则返回false
     */
    public boolean contains(K key) {
        return get(key) != null;
    }

    /**
     * 清空所有键值对
     */
    public void clear() {
        root = null;
    }

    /**
     * 判断树是否为空
     * @return 如果集合为空,返回true;否则返回false
     */
    public boolean isEmpty() {
        return root == null;
    }

    /**
     * 获取键值对的个数
     * @return 键值对的个数
     */
    public int size() {
        return size(root); // 避免空指针异常
    }
    public Set<K> keys() {
        Set<K> set = new LinkedHashSet<>();
        inOrder(root,set);
        return set;
    }

    private void inOrder(TreeNode x, Set<K> set) {
        if (x == null) return;
        inOrder(x.left, set);
        set.add(x.key);
        inOrder(x.right, set);
    }
    public void delete(K key) {
        if (key == null) {
            throw new IllegalArgumentException("Key cannot be null");
        }
        if (!contains(key)) return ;
        // 删除指定的键值对
        if(!isRed(root.left)) root.color = RED;
        root = delete(root, key);
        if (!isEmpty()) {
            root.color = BLACK;
        }
        check();
    }

    private TreeNode delete(TreeNode x, K key) {
        if (key.compareTo(x.key) < 0) {
            if (!isRed(x.left) && !isRed(x.left.left)) {
                x = moveRedLeft(x);
            }
            x.left = delete(x.left, key);
        } else {
            // 为了方便删除,右旋左倾的红色链接
            if (isRed(x.left)) x = rotateRight(x);
            // 在底部删结点
            if (key.compareTo(x.key) == 0 && x.right == null) {
                return null;
            }
            // 如果右孩子是2-node,需要从左孩子中借结点
            if (!isRed(x.right) && !isRed(x.right.left)) {
                x = moveRedRight(x);
            }
            if (key.compareTo(x.key) == 0) {
                TreeNode minOfRight = min(x.right);
                x.key = minOfRight.key;
                x.value = minOfRight.value;

                x.right = deleteMin(x.right);
            } else {
                x.right = delete(x.right, key);
            }
        }
        return fixUp(x);
    }
    /***********************************************************
     *                     ordered methods
     **********************************************************/
    /**
     * 获取键的最小值
     * @return 键的最小值
     */
    public K min() {
        if(isEmpty()){
            throw new NoSuchElementException("Tree is Empty!");
        }
        return min(root).key;   //min(root)查找最小结点
    }
    // 调用这个方法时node不能为null
    private TreeNode min (TreeNode node) {
        TreeNode x = node;
        while (x.left != null) x = x.left;
        return x;
    }
    public K max() {
        if(isEmpty()){
            throw new NoSuchElementException("Tree is Empty!");
        }
        return max(root).key;   //min(root)查找最大结点
    }
    private TreeNode max (TreeNode node) {
        TreeNode x = node;
        while (x.right != null) x = x.right;
        return x;
    }

    public void deleteMax() {
        if (isEmpty()) {
            throw new NoSuchElementException("The tree is empty!");
        }
        if (!isRed(root.left)) root.color = RED;  //保证这个结点是红色的。则前提条件得到了满足(moveRedRight)
        root = deleteMax(root);
        if (!isEmpty()) root.color = BLACK;  //自底向上分解4-node,可能导致根结点变红,所以需要这一步
        check();
    }

    private TreeNode deleteMax(TreeNode x) {
        // 右旋左倾的红色链接
        if (isRed(x.left)) {
            x = rotateRight(x);  //判断是否有左倾红色结点
        }
        if (x.right == null) return null;  //判断右子树是否为null(因为完美平衡,所以右结点没有,左结点也没有,直接删除)
        if(!isRed(x.right) && !isRed(x.right.left)) {  //判断右孩子是否为2-node,是则从兄弟结点借结点。
            x = moveRedRight(x);
        }
        x.right = deleteMax(x.right);  // 递归删除右子树的最大值
        return fixUp(x); // 自底向上修复
    }

    /**
     * 删除最小值
     */
    private void deleteMin() {
        if (isEmpty()) {
            throw new NoSuchElementException("The tree is Empty!");
        }
        if (!isRed(root.left)) root.color = RED; // 染红根结点
        root = deleteMin(root);
        if(!isEmpty()) root.color = BLACK;
        check();
    }

    private TreeNode deleteMin(TreeNode x) {
        if (x.left == null) return null; //底部删除最小值
        if(!isRed(x.left) && !isRed(x.left.left)) {
            x = moveRedLeft(x);   //左孩子是2-node,从右孩子借结点
        }
        // 往左走
        x.left = deleteMin(x.left);
        return fixUp(x);
    }

    /**
     * 获取小于等于指定值key的最大键
     * @param key 指定值
     * @return 小于等于指定值的最大键
     */
    public K floor(K key) {
        if (key == null) {
            throw new IllegalArgumentException("Key cannot be null.");
        }
        TreeNode x = floor(root, key);
        if (x == null) return null;
        return x.key;
    }
    private TreeNode floor(TreeNode x, K key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);
        if (cmp == 0) return x;
        if (cmp < 0) return floor(x.left, key);
        // key > x.key,x就是备选
        TreeNode t = floor(x.right, key);
        if(t != null) return t;
        return x;
    }

    /**
     * 返回大于等于指定值key的最小键
     * @param key 指定值
     * @return 大于等于key的最小键
     */
    private K ceiling(K key) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null.");
        }
        TreeNode x = ceiling(root, key);
        if (x == null) return null;
        else return x.key;
    }

    private TreeNode ceiling(TreeNode x, K key) {
        if (x == null) return null;
        int cmp = key.compareTo(x.key);
        if (cmp == 0) return x;
        if (cmp > 0) return ceiling(x.right, key);
        TreeNode t = ceiling(x.left, key);
        if (t != null) return t;
        return x;
    }

    /**
     *  获取键的排名(小于key的键的个数)
     * @param key 键
     * @return 键的排名
     */
    public int rank(K key) {
        if (key == null) {
            throw new IllegalArgumentException("Key cannot be null.");
        }
        return rank(root, key);
    }
    private int rank(TreeNode x, K key) {
        if (x == null) return 0;
        int cmp = key.compareTo(x.key);
        if (cmp == 0) return size(x.left);
        if (cmp < 0) return rank(x.left, key);
        return rank(x.right, key) + 1 + size(x.left);
    }

    /**
     * 获取排名为k的键
     * @param k 排名
     * @return 排名为k的键
     */
    public K select(int k) {
        if (k < 0 || k >= size()) {
            throw new IndexOutOfBoundsException("k = " + k + ", size = " + size());
        }
        return select(root, k).key;
    }

    private TreeNode select(TreeNode x, int k) {
        int rank = size(x.left);
        if (k == rank) return x;
        if (k < rank) return select(x.left, k);
        return select(x.right, k - rank - 1);
    }

    /**
     * 获取大于等于low,小于等于high的键的个数
     * @param low 下界
     * @param high 上界
     * @return 键的个数
     */
    public int size(K low, K high) {
        if (low == null || high == null) {
            throw new IllegalArgumentException("Low or high cannot be null.");
        }
        if (low.compareTo(high) > 0) {
            throw new IllegalArgumentException("Low cannot be larger than high.");
        }
        int r1 = rank(low);   // 小于low的元素个数
        int r2 = rank(high);  // 小于high的元素个数
        //[low, high]
        if (contains(high)) return r2 - r1 + 1;   //画轴
        return r2 - r1;
    }

    /**
     * 获取大于等于low,小于等于high的键的集合
     * @param low 下界
     * @param high 上界
     * @return 键的集合
     */
    public Set<K> keys(K low, K high) {
        if (low == null || high == null) {
            throw new IllegalArgumentException("Low or high cannot be null.");
        }
        if (low.compareTo(high) > 0) {
            throw new IllegalArgumentException("Low cannot be larger than high.");
        }
        Set<K> set = new LinkedHashSet<>();
        inOrder(root, low, high, set);
        return set;
    }

    private void inOrder(TreeNode x, K low, K high, Set<K> set) {
        if (x == null) return;
        int cmp1 = low.compareTo(x.key);
        int cmp2 = high.compareTo(x.key);
        if (cmp1 < 0) inOrder(x.left, low, high, set); //遍历左子树(剪枝)
        if (cmp1 <= 0 && cmp2 >= 0) set.add(x.key);  //添加根结点
        if (cmp2 > 0) inOrder(x.right, low, high, set); // 遍历右子树(剪枝)
    }

    /***********************************************************
     *                    helper methods
     **********************************************************/
    private boolean isRed(TreeNode x) {
        if (x == null) return false;
        return x.color == RED;
    }

    private int size(TreeNode x) {
        if (x == null) return 0;
        return x.size;
    }
    //左旋,右旋
    private TreeNode rotateLeft(TreeNode h) {
        // 调整结构
        TreeNode x =h.right;
        h.right = x.left;
        x.left = h;
        // 调整颜色
        x.color = x.left.color;
        h.color = RED;
        // 调整size
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;  //防止子树为空
        return x;
    }
    private TreeNode rotateRight(TreeNode h) {
        TreeNode x = h.left;
        h.left = x.right;
        x.right = h;

        x.color = x.right.color;
        h.color = RED;

        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }
    public void flipColors(TreeNode x) {
        x.color = !x.color;
        x.left.color = !x.left.color;
        x.right.color = !x.right.color;
    }
    private TreeNode fixUp (TreeNode x) {
        x.size = size(x.left) + size(x.right) + 1;  //修复,先该其size的值
        if(isRed(x.right)) {  // 判断有没有右倾红色结点
            x = rotateLeft(x);
        }
        if(isRed(x.left) && isRed(x.left.left)) {  //判断有没有不标准的4-node
            x = rotateRight(x);
        }

        if (isRed(x.left) && isRed(x.right)) {  // 删除4-node。RBT不会存在4-node
            flipColors(x);
        }
        return x;
    }
    private TreeNode moveRedRight(TreeNode x) {  //向左兄弟结点借结点
        flipColors(x);  //先变色
        if(isRed(x.left.left)) {   //再判断是否为4-node
            x = rotateRight(x);  //是的话,右转,再变色
            flipColors(x);
        }
        return x;
    }

    private TreeNode moveRedLeft(TreeNode x) {   //向右兄弟结点借结点
        flipColors(x);
        if(isRed(x.right.left)) {
            x.right = rotateRight(x.right);
            x = rotateLeft(x);
            flipColors(x);
        }
        return x;
    }


    /***********************************************************
     *                     check methods
     **********************************************************/
    private boolean check() {
        boolean isBST = isBST();
        if (!isBST) System.err.println("The tree is not a BST!");
        boolean is23 = is23();
        if (!is23) System.err.println("The tree is not a 2-3 tree!");
        boolean isBalanced = isBalanced();
        if(!isBalanced) System.err.println("The tree is not balanced!");
        return isBST && is23 && isBalanced;
    }

    private boolean isBalanced() {
        // 先求从根结点到某个叶子结点的路劲长
        int height = 0;
        TreeNode x = root;
        while (x != null) {
            if (!isRed(x)) height++;
            x = x.left;
        }
        return isBalanced(root,height);  // 判断从根结点到所有叶子结点的高度是不是等于height
    }

    private boolean isBalanced(TreeNode x, int height) {
        if (x == null) return height == 0;  // x为结点则说明为叶结点,此时如果传入的height = 0了,则说明高度相同
        if (!isRed(x)) height--;
        return isBalanced(x.left, height) && isBalanced(x.right, height);
    }

    private boolean is23() {
        return !isRed(root) && is23(root);
    }
    private boolean is23(TreeNode x) {
        if (x == null) return true;
        if (isRed(x.right)) return false; //要求3-node必须左倾
        if (isRed(x.left) && isRed(x.left.left)) return false;
        return is23(x.left) && is23(x.right);
    }

    private boolean isBST() {
        return isBST(root);
    }

    private boolean isBST(TreeNode x) {
        if (x == null) return true;
        if (x.left != null) {
            TreeNode maxOfLeft = max(x.left);
            if (x.key.compareTo(maxOfLeft.key) <= 0) return false;
        }
        if (x.right != null) {
            TreeNode minOfRight = min(x.right);
            if (x.key.compareTo(minOfRight.key) >= 0) return false;
        }
        return isBST(x.left) && isBST(x.right);
    }

    public static void main(String[] args) {
        // void put(K key, V value)
        RedBlackTree<Character, Object> tree = new RedBlackTree<>();
        Object dummyValue = "刘亦菲";
        tree.put('A', dummyValue);
        tree.put('S', dummyValue);
        tree.put('E', dummyValue);
        tree.put('R', dummyValue);
        tree.put('C', dummyValue);
        tree.put('D', dummyValue);
        tree.put('I', dummyValue);
        tree.put('N', dummyValue);
        tree.put('B', dummyValue);
        tree.put('X', dummyValue);
        // System.out.println(tree.check()); true

        //void clear(), int size(), boolean isEmpty()
//        System.out.println(tree.keys());
//        System.out.println(tree.size());
//        System.out.println(tree.isEmpty());
//        tree.clear();
//        System.out.println(tree.keys());
//        System.out.println(tree.size());
//        System.out.println(tree.isEmpty());

        // V get(K key)
//        System.out.println(tree.get('A'));
//        System.out.println(tree.get('Z'));
//        //boolean contains(K key)
//        System.out.println(tree.contains('A'));
//        System.out.println(tree.contains('Z'));

        //K max()  K min()
//        System.out.println(tree.min());
//        System.out.println(tree.max());
//        tree.clear();
//        System.out.println(tree.min());
//        System.out.println(tree.max());

        // void deleteMax()
//        System.out.println(tree.keys());
//        while(!tree.isEmpty()) {
//            tree.deleteMax();
//            System.out.println(tree.keys());
//        }
//        System.out.println(tree.keys());
//        tree.delete('Z');
//        System.out.println(tree.keys());
//        tree.delete('I');
//        System.out.println(tree.keys());

//        Set<Character> set = tree.keys();
//        List<Character> list = new ArrayList<>(set);
//        Collections.shuffle(list);  //打乱
//        System.out.println(set);
//        for (Character c : list) {
//            System.out.println(c);
//            tree.delete(c);
//            System.out.println(tree.keys());
//        }

        // K floor(K key)
//        System.out.println(tree.keys());
//        System.out.println(tree.floor('I'));
//        System.out.println(tree.floor('M'));
//        System.out.println(tree.floor('0'));

        //K ceiling(K key)
//        System.out.println(tree.keys());
//        System.out.println(tree.ceiling('I'));
//        System.out.println(tree.ceiling('M'));
//        System.out.println(tree.ceiling('Z'));

        // int rank(K key)
//        System.out.println(tree.keys());
//        System.out.println(tree.rank('I'));
//        System.out.println(tree.rank('M'));
//        System.out.println(tree.rank('Z'));
//        System.out.println(tree.rank('0'));
        //K select(int K)
//        System.out.println(tree.keys());
//        System.out.println(tree.select(0));
//        System.out.println(tree.select(5));
//        System.out.println(tree.select(9));

        // Set<K> keys(K low, K high)
        System.out.println(tree.keys());
        System.out.println(tree.keys('C', 'O'));
        System.out.println(tree.keys('B', 'N'));
    }

}

作业

package day15;
/*
给定一棵二叉树,判断它是不是镜像对称的。
比如:
    1
  / \
 2   2
/ \ / \
3  4 4  3
这是镜像对称的
    1
  / \
 2   2
  \   \
  3    3
这不是镜像对称的

 */
public class Ex1 {
    public boolean isSymmetric(TreeNode x) {
        return isSymmetric(x, x);
    }

    private class TreeNode {
        TreeNode left;
        TreeNode right;
        int value;
        public TreeNode(int value) {
            this.value = value;
        }
    }

    private boolean isSymmetric(TreeNode x1, TreeNode x2) {
        if (x1 == null && x2 == null) return true;
        if (x1 == null || x2 == null) return false;
        return x1.value == x2.value && isSymmetric(x1.left, x2.right) && isSymmetric(x1.right, x2.left);
    }

}

B树

红黑树就是3阶或者4阶B树

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页