红黑树学习笔记

学习视频

https://www.bilibili.com/video/BV1Jp411R7Sb
白嫖至B站子空Kosora UP主

1. 复习一下二叉树

leetcode:第226题:翻转二叉树
在这里插入图片描述

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root != null) {
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
            invertTree(root.left);
            invertTree(root.right);
        }
        return root;
    }
}
2. 复习二叉搜素树
// 节点类
public class AVLEntry<K, V> implements Map.Entry<K, V> {

    public K key;
    public V value;
    public AVLEntry<K, V> left;
    public AVLEntry<K, V> right;
    public int height = 1;

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }

    @Override
    public V setValue(V value) {
        this.value = value;
        return value;
    }

    public AVLEntry() {
    }

    public AVLEntry(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public AVLEntry(K key, V value, AVLEntry<K, V> left, AVLEntry<K, V> right, int height) {
        this.key = key;
        this.value = value;
        this.left = left;
        this.right = right;
        this.height = height;
    }

    @Override
    public String toString() {
        return "AVLEntry{" +
                "key=" + key +
                ", value=" + value +
                '}';
    }
}

public class AVLMap<K, V> implements Iterable<AVLEntry<K, V>> {

    private int size;
    private AVLEntry<K, V> root;
    private Comparator<K> comp;

    @SuppressWarnings("unchecked")
    private int compare(K a, K b) {
        if (comp != null) {
            return comp.compare(a, b);
        }
        else {
            Comparable<K> c = (Comparable<K>) a;
            return c.compareTo(b);
        }
    }

    public AVLMap(Comparator<K> comp) {
        super();
        this.comp = comp;
    }

    public AVLMap() {
        super();
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0 ? true:false;
    }

    public V put(K key, V value) {
        V returnValue = value;
        if (root == null) {
            root = new AVLEntry<K, V>(key, value);
            size++;
        }
        else {
            AVLEntry<K, V> p = root;
            while (p != null) {
                int compareResult = compare(key, p.key);
                if (compareResult == 0) {
                    returnValue = p.getValue();
                    p.setValue(value);
                    break;
                }
                else if (compareResult < 0) {
                    if (p.left == null) {
                        p.left = new AVLEntry<K, V>(key, value);
                        size++;
                        break;
                    }
                    else {
                        p = p.left;
                    }
                }
                else {
                    if (p.right == null) {
                        p.right = new AVLEntry<K, V>(key, value);
                        size++;
                        break;
                    }
                    else {
                        p = p.right;
                    }
                }
            }
        }
        return returnValue;
    }

    @Override
    public Iterator<AVLEntry<K, V>> iterator() {
        return new AVLIterator<>(root);
    }

    private AVLEntry<K, V> getEntry(K key) {
        AVLEntry<K, V> p = root;
        while (p != null) {
            int compareResult = compare(key, p.key);
            if (compareResult == 0) {
                return p;
            }
            else if (compareResult < 0) {
                p = p.left;
            }
            else {
                p = p.right;
            }
        }
        return null;
    }

    public boolean containsKey(K key) {
        return getEntry(key) != null;
    }

    public V get(K key) {
        AVLEntry<K, V> p = getEntry(key);
        return p != null ? p.getValue() : null;
    }

    public boolean containsValue(V value) {
        Iterator<AVLEntry<K, V>> itr = this.iterator();
        while (itr.hasNext()) {
            if (itr.next().getValue().equals(value)) {
                return true;
            }
        }
        return false;
    }

    public AVLEntry<K, V> getFirstEntry(AVLEntry<K, V> p) {
        if (p == null) {
            return p;
        }
        while ((p.left != null)) {
            p = p.left;
        }
        return p;
    }

    public AVLEntry<K, V> getLastEntry(AVLEntry<K, V> p) {
        if (p == null) {
            return p;
        }
        while ((p.right != null)) {
            p = p.right;
        }
        return p;
    }

    private AVLEntry<K, V> deleteEntry(AVLEntry<K, V> p, K key) {
        if (p == null) {
            return null;
        }
        else {
            int compareResult = compare(key, p.key);
            if (compareResult == 0) {
                if (p.left == null && p.right == null) {
                    p = null;
                }
                else if (p.left != null && p.right == null) {
                    p = p.left;
                }
                else if (p.right != null && p.left == null) {
                    p = p.right;
                }
                else {
                    // 随机取右边最小值还是左边最大值代替该节点
                    if ((size & 1) == 0) {
                        AVLEntry<K, V> rightMin = getFirstEntry(p.right);
                        p.key = rightMin.key;
                        p.value = rightMin.value;
                        // 删掉右边最小值,因为它的值已经被存储
                        AVLEntry<K, V> newRight = deleteEntry(p.right, p.key);
                        p.right = newRight;
                    }
                    else {
                        AVLEntry<K, V> leftMax = getLastEntry(p.left);
                        p.key = leftMax.key;
                        p.value = leftMax.value;
                        // 删掉左边最大值,因为它的值已经被存储
                        AVLEntry<K, V> newLeft = deleteEntry(p.left, p.key);
                        // 因为子树结构可能被改变,所以要重新赋值子树?是不是多余拉?引用地址改变拉。所以要重新赋值
                        p.left = newLeft;
                    }
                }
            }
            else if (compareResult < 0) {
                AVLEntry<K, V> newLeft = deleteEntry(p.left, key);
                p.left = newLeft;
            }
            else {
                AVLEntry<K, V> newRight = deleteEntry(p.right, key);
                p.right = newRight;
            }
            return p;
        }
    }

    public V remove(K key) {
        AVLEntry<K, V> entry = getEntry(key);
        if (entry == null) {
            return null;
        }
        V oldValue = entry.getValue();
        root = deleteEntry(root, key);
        size--;
        return oldValue;
    }

    public void levelOrder() {
        Queue<AVLEntry<K, V>> queue = new LinkedList<AVLEntry<K, V>>();
        queue.offer(root);
        int preCount = 1;
        int pCount = 0;
        while (!queue.isEmpty()) {
            AVLEntry<K, V> p = queue.poll();
            System.out.print(p + " ");
            if (p.left != null) {
                queue.offer(p.left);
                pCount++;
            }
            if (p.right != null) {
                queue.offer(p.right);
                pCount++;
            }
            preCount--;
            if (preCount == 0) {
                preCount = pCount;
                pCount = 0;
                System.out.println();
            }
        }
    }

}

lintcode 第448题:二叉查找树的中序后继
在这里插入图片描述

public class Solution {
    /*
     * @param root: The root of the BST.
     * @param p: You need find the successor node of p.
     * @return: Successor of p.
     */
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        // p为空或者p就是最大的点返回null
        if (p == null || getLastNode(root) == p) {
            return null;
        }

        // p有右子树,则它的后继点为右子树的最小值
        if (p.right != null) {
            return getFirstNode(p.right);
        }

        // p没有右子树,则找到根节点到p店路径的最后一个左拐的点
        TreeNode parent = root;
        TreeNode tmp = root;
        while (parent != null) {
            if (parent == p) {
                break;
            }
            if (parent.val > p.val) {
                tmp = parent;
                parent = parent.left;
            }
            else {
                parent = parent.right;
            }
        }
        return tmp;

    }

    private TreeNode getFirstNode(TreeNode p) {
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }

    private TreeNode getLastNode(TreeNode root) {
        while (root.right != null) {
            root = root.right;
        }
        return root;
    }
}
3. 复习AVL平衡二叉树

leetcode:1382题,将二叉搜索树变平衡
在这里插入图片描述

class Solution {

        private TreeNode root;
        public LinkedList<TreeNode> stack = new LinkedList<>();
        private HashMap<TreeNode, Integer> heightMap = new HashMap<>();

        public TreeNode balanceBST(TreeNode root) {
            Stack<TreeNode> stack = new Stack<>();
            stack.add(root);
            while (!stack.isEmpty()) {
                TreeNode p = stack.pop();
                put(new TreeNode(p.val));
                if (p.left != null) {
                    stack.add(p.left);
                }
                if (p.right != null) {
                    stack.add(p.right);
                }
            }
            return this.root;
        }

        private void put(TreeNode p) {
            if (root == null) {
                root = p;
                stack.push(root);
            }
            else {
                TreeNode parent = root;
                while (true) {
                    stack.push(parent);

                    if (p.val < parent.val) {
                        if (parent.left == null) {
                            parent.left = p;
                            stack.push(p);
                            break;
                        }
                        else {
                            parent = parent.left;
                        }
                    }
                    else {
                        if (parent.right == null) {
                            parent.right = p;
                            stack.push(p);
                            break;
                        }
                        else {
                            parent = parent.right;
                        }
                    }
                }
            }
            fixAfterInsertion(p);
        }

        private int getHeight(TreeNode p) {
            return heightMap.containsKey(p) ? heightMap.get(p) : 0;
        }

        private void fixAfterInsertion(TreeNode p) {
            TreeNode parent = root;
            while (!stack.isEmpty()) {
                parent = stack.pop();
                int newHeight = Math.max(getHeight(parent.left), getHeight(parent.right)) + 1;
                if (heightMap.containsKey(parent) && getHeight(parent) > 1 && newHeight == getHeight(parent)) {
                    stack.clear();
                    return;
                }
                heightMap.put(parent, newHeight);
                int d = getHeight(parent.left) - getHeight(parent.right);
                if (Math.abs(d) <= 1) {
                    continue;
                }
                else {
                    if (d == 2) {
                        if (p.val - parent.left.val < 0) {
                            parent = rotateRight(parent);
                        }
                        else {
                            parent = rotateLeftRight(parent);
                        }
                    }
                    else {
                        if (p.val - parent.right.val > 0) {
                            parent = rotateLeft(parent);
                        }
                        else {
                            parent = rotateRightLeft(parent);
                        }
                    }
                    if (!stack.isEmpty()) {
                        if (p.val - stack.peek().val < 0) {
                            stack.peek().left = parent;
                        }
                        else {
                            stack.peek().right = parent;
                        }
                    }
                }
            }
            root = parent;
        }

        private TreeNode rotateRight(TreeNode parent) {
            TreeNode left = parent.left;
            parent.left = left.right;
            left.right = parent;
            heightMap.put(parent, Math.max(getHeight(parent.left), getHeight(parent.right)) + 1);
            heightMap.put(left, Math.max(getHeight(left.left), heightMap.get(parent)) + 1);
            return left;
        }

        private TreeNode rotateLeft(TreeNode parent) {
            TreeNode right = parent.right;
            parent.right = right.left;
            right.left = parent;
            heightMap.put(parent, Math.max(getHeight(parent.left), getHeight(parent.right)) + 1);
            heightMap.put(right, Math.max(getHeight(right.right), heightMap.get(parent)) + 1);
            return right;
        }

        private TreeNode rotateLeftRight(TreeNode parent) {
            parent.left = rotateLeft(parent.left);
            parent = rotateRight(parent);
            return parent;
        }

        private TreeNode rotateRightLeft(TreeNode parent) {
            parent.right = rotateRight(parent.right);
            parent = rotateLeft(parent);
            return parent;
        }

}
4. 学习红黑树
  • 1 每个节点要么是红的,要么是黑的
  • 2 根节点是黑的
  • 3 定义NULL为黑色
  • 4 如果某个子节点是红色,那么它的两个儿子都是黑色,且父节点也必定是黑色
  • 5 对于任一节点而言,它到叶节点的每一条路劲都包含相同数目的黑色节点,通常称为黑高,BlackHeight
  • RBT是一个BST
  • 任意一棵以黑色节点为根的子树也必定是一棵红黑树
    在这里插入图片描述

结论:

  • 红黑树不像AVL一样,永远保持绝对平衡
  • 相对平衡
  • 若H(left) >= H(right),则:H(left) <= 2 * H(right) + 1, 但BH(left) == BH(right), H(left) < H(right)同理
  • 定理:N个节点的RBT,最大高度是2log(N + 1)
  • 严格证明参考CLRS
  • 查询效率AVL略好于RBT
  • 插入删除效率RBT高于AVL
插入原则
  • 若插入的节点为黑色,肯定违反性质5
  • 只能插入红色节点,可能违反性质4,继续调整
  • 每将节点进行染色、旋转操作,我们需要考虑:
    *是否会引起左右子树BH不一致
    *有无可能继续破环性质4的可能
    在这里插入图片描述
  1. 无需调整
  • 越界
  • X是根节点
  • 父节点P的颜色为黑,必定满足性质4
  • 最后一步:将根节点root染黑
  1. case1
  • 条件: P为G的左孩子,Y为红色,X可左可右
  • 处理方式: P、Y染黑,G染红,X回溯至G
  • 条件简称: 红左父、红叔、红左右子
  • 处理方式简称: 父叔都变黑、爷变红、子变爷
    在这里插入图片描述
    因为回溯到了G点,G是一个红色节点,所以它可以变成其他任意情况

2. case2

  • 条件: P为G的左孩子,Y为黑色,X为有孩子
  • 处理方式: 左旋P,X指向P,转换为case3
  • 条件简称: 红左父,黑叔,红右子
  • 处理方式简称: 左旋父, 子变父, 变为case3
    在这里插入图片描述
    case2只能转化为case3, case2不会引起BH增加
  1. case3
  • 条件: P为G的左孩子,Y为黑色,X为左孩子
  • 处理方式: P染黑,G染红,右旋G,结束
  • 条件简称: 红左父,黑叔,红左子
  • 处理方式简称: 父变黑,爷变红,右旋爷
    在这里插入图片描述
    case3无需转化,也不会引起BH增加
    RBT插入效率优于AVL
    AVL查询效率优于RBT

这是P为G的左孩子情况,右孩子对称即可.
在这里插入图片描述

删除原则

删除是先根据二叉搜索树的特点将该节点的后继节点的值替换该节点,然后删除该后继节点

  • 删除红色节点,不会影响BH,也不会影响BH,也不会违反性质4,无需调整

  • 删除黑色节点,节点所在子树的BH–,需要调整
    在这里插入图片描述

  • 需要删除的节点为红色,直接删除

  • 其它无需调整的情况为:
    当前X为根节点,无论root什么颜色,都将root染黑,rootOver
    当前X为红色,将X染黑,结束,redOver

  • 删除左孩子X,分为4种情况
    在这里插入图片描述
    先记到这里拉…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值