[Loj.ac-java] RB Tree - 左偏红黑树

问题:

说明:

https://oi-wiki.org/ds/llrbt/

来 写 一 个 红 黑 树。

问题链接:https://loj.ac/problem/104

提交记录:

https://loj.ac/submission/917134

输入案例:

传了一份测试数据:https://download.csdn.net/download/qq_28033719/12709334


我的代码:

准备知识

1、以往知识:

AVL Tree - 元老级别的平衡树 https://blog.csdn.net/qq_28033719/article/details/107974225

2、二三树:

1) 性质:

        a、二三树是绝对平衡树,高度绝对一致。

        b、二三树一个节点,可以存放一个或者两个元素

        c、因为绝对平衡,所以到达任意 叶子节点 都经过相同的 节点数。

2) 插入情况:

        a、如果插入到了一个只有元素数量 1 的叶子节点(我把根节点拍红色了)

        b、如果插入了一个元素数量2,根节点数量1的节点,虽然三个叉,和红黑依然不相同

        c、如果父节点 元素数量 2,插入一个叶子节点数量也是2,貌似二三树和红黑树差别还是很大。

可以说,如果把二三树一些节点标记为红色,那么就能够慢慢形成,红黑树的一些概念,红色节点是一个辅助节点,用来标记这个节点是一个辅助黑色节点成为两个元素节点的节点。

3、左偏红黑树:

相对二三树来说,红黑树是比较容易实现的数据结构,因为二三树有三叉,四叉,二节点,三节点这样的存放问题,所以增加删除逻辑都更加难看,红黑树来说,简化了二三树的逻辑。

那么左偏红黑树可以说更加简化红黑树逻辑,毕竟从节点为红色,变成了连接枝为红色。而且只允许左节点连接是红色,所以就是左偏红黑树。

那么写法上,也是拿着二叉搜索树的模板,然后进行修改,多了一个 color 属性,而且要处理增加,删除一些特例:

1) 两个连续的左连接是红色

2) 经过旋转,左右连个节点连接是红色

3) 在 2) 情况下,右节点还有一个左节点红色

import java.util.Scanner;

public class RedBlackTree {
    private static boolean RED = true;
    private static boolean BLC = false;
    class RBNode {
        boolean color;
        RBNode right,left;
        int val, num, size;
        RBNode(int val) {this.val = val; this.num = this.size = 1; this.color = RED;}
    }

    RBNode ROOT;

    public void pushup(RBNode r) {
        if(r != null) {
            int left = r.left == null ? 0 : r.left.size;
            int right = r.right == null ? 0 : r.right.size;
            r.size = r.num + left + right;
        }
    }

    private void cleanUp(RBNode r) {
        if(r != null) {
            cleanUp(r.left);
            cleanUp(r.right);
            r.left = r.right = null;
        }
    }

    /**
     * 和二叉搜索树一样插入节点, 新节点默认红色
     */
    public RBNode insert(RBNode root, int val) {
        root = insert_inner(root, val);
        root.color = BLC; // 根节点改为黑色
        return root;
    }

    public RBNode insert_inner(RBNode root, int val) {
        if(root == null) return new RBNode(val);
        if(root.val == val) {
            root.num ++; root.size ++; return root;
        }
        else if(root.val < val) root.right = insert_inner(root.right, val);
        else root.left = insert_inner(root.left, val);
        return fixUp(root); // 对红黑节点进行平衡
    }

    /**
     * 进行颜色平衡
     */
    public RBNode fixUp(RBNode root) {
        if(root == null) return root;
        // 处理标记
        boolean leftRed = root.left != null
                        && root.left.color == RED;
        boolean leftLeftRed = root.left != null
                            && root.left.left != null
                            && root.left.left.color == RED;
        boolean rightRed = root.right != null
                        && root.right.color == RED;

        // 先判断是否只是右边的红色
        if(rightRed && !leftRed) root = rotate(root, true);
        // 在判断是否连续红色,因为考虑到旋转后的问题
        if(leftRed && leftLeftRed) root = rotate(root, false);
        // 最后判断是否两边红色,因为考虑到上面两次旋转后的问题
        if(leftRed && rightRed) colorFlip(root);
        pushup(root);
        return root;
    }

    /**
     * 反转颜色, 当双边都红色的时候
     */
    public void colorFlip(RBNode root) {
        root.color = RED;
        if(root.left != null) root.left.color = BLC;
        if(root.right != null) root.right.color = BLC;
    }

    /**
     * dir = true (左旋) | flase (右旋)
     */
    public RBNode rotate(RBNode root, boolean dir) {
        RBNode temp;
        if(dir) {
            temp = root.right;
            root.right = temp.left;
            temp.left = root;
        } else {
            temp = root.left;
            root.left = temp.right;
            temp.right = root;
        }
        temp.color = root.color; // 旋转中需要修改颜色
        root.color = RED;
        pushup(root); pushup(temp); return temp;
    }

    public RBNode delete(RBNode root, int val) {
        if(root == null) return root;
        // 获取标记
        boolean leftRed = root.left != null
                        && root.left.color == RED;
        boolean leftLeftRed = root.left != null
                            && root.left.left != null
                            && root.left.left.color == RED;
        boolean rightRed = root.right != null
                        && root.right.color == RED;
        boolean rightLeftRed = root.right != null
                            && root.right.left != null
                            && root.right.left.color == RED;

        // 对标记进行处理
        if(root.val > val) {
            if(!leftRed && !leftLeftRed) root = moveRedLeft(root);
            root.left = delete(root.left, val);
        } else {
            if(leftRed) root = rotate(root, false);
            if(root.val == val && root.right == null) {
                root.num --; root.size --;
                if(root.num == 0) return root.left;
                return root;
            }
            if(!rightRed && !rightLeftRed) root = moveRedRight(root);
            if(root.val == val) {
                root.num --;
                if(root.num == 0) {
                    exchange(root, getMin(root.right));
                    root.right = deleteMin(root.right);
                } pushup(root);
                return root;
            } else { // root.val > val
                root.right = delete(root.right, val);
            }
        }
        return fixUp(root);
    }

    private RBNode deleteMin(RBNode r) {
        if (r.left == null) return r.right;

        boolean leftRed = r.left.color == RED;
        boolean leftLeftRed = r.left.left != null && r.left.left.color == RED;
        if (!leftRed && !leftLeftRed) {
            r = moveRedLeft(r);
        }
        r.left = deleteMin(r.left);
        pushup(r);
        return fixUp(r);
    }

    private RBNode getMin(RBNode r) {
        while(r.left != null) r = r.left;
        return r;
    }

    private void exchange(RBNode r, RBNode e) {
        r.val = e.val; r.num = e.num;
        pushup(r);
    }

    public RBNode moveRedLeft(RBNode r) {
        colorFlip(r);
        // 处理标记
        boolean rightLeftRed = r.right != null
                && r.right.left != null
                && r.right.left.color == RED;

        if(rightLeftRed) {
            r.right = rotate(r.right, false);
            r = rotate(r, true);
            colorFlip(r);
        }
        return r;
    }

    public RBNode moveRedRight(RBNode r) {
        colorFlip(r);
        // 处理标记
        boolean leftLeftRed = r.left != null
                && r.left.left != null
                && r.left.left.color == RED;

        if(leftLeftRed) {
            r = rotate(r, false);
            colorFlip(r);
        }
        return r;
    }

    /**
     * 找排名 n
     */
    public int find(RBNode r, int n) {
        if(r == null) return 0;
        int left = r.left == null ? 0 : r.left.size;
        if(left >= n) return find(r.left, n);
        else if(left + r.num < n) return find(r.right, n - left - r.num);
        else return r.val;
    }

    /**
     * 找值 v 排名
     */
    public int rank(RBNode r, int v) {
        if(r == null) return 1;
        else if(r.val > v) return rank(r.left, v);
        else if(r.val < v) return rank(r.right, v) + (r.left == null ? 0 : r.left.size) + r.num;
        else return r.left == null ? 1 : r.left.size + 1;
    }

    /**
     * 前驱 最大的 小值
     */
    public int pre(RBNode r, int v) {
        if(r == null) return Integer.MIN_VALUE;
        else if(r.val >= v) return pre(r.left, v);
        else return Math.max(pre(r.right, v), r.val);
    }

    /**
     * 后继 最小的 大值
     */
    public int sub(RBNode r, int v) {
        if(r == null) return Integer.MAX_VALUE;
        else if(r.val <= v) return sub(r.right, v);
        else return Math.min(sub(r.left, v), r.val);
    }

    private static int cur = 0;
    private static int pre = 1;
    private static int top1 = 0;
    private static String ll = "(";
    private static String rr = ")";
    private static String bb = "    ";
    private static StringBuilder builder = new StringBuilder();
    private static RBNode[][] stack = new RBNode[2][(int)Math.pow(2,12)];
    public String printTree() {
        RBNode r = ROOT;
        if(r != null) stack[cur][top1 ++] = r;
        while(top1 != 0) {
            int top2 = 0;
            for(int i = 0;i < top1;i ++) {
                RBNode temp = stack[cur][i];
                builder.append(temp.val).append(ll).append(temp.color).append(rr).append(bb);
                if(temp.left != null) stack[pre][top2 ++] = temp.left;
                if(temp.right != null) stack[pre][top2 ++] = temp.right;
            }
            builder.append("\r\n");
            cur ^= 1;
            pre ^= 1;
            top1 = top2;
        }
        String res = builder.toString();
        builder.setLength(0);
        return res;
    }

    private static Scanner sc = new Scanner(System.in);
    private static int num;
    public static void main(String[] args) {
        num = sc.nextInt();
        RedBlackTree tree = new RedBlackTree();
        while(num -- > 0) {
            int op = sc.nextInt();
            int val = sc.nextInt();
            switch (op) {
                case 1:
                    tree.ROOT = tree.insert(tree.ROOT, val);
                    System.out.println(tree.printTree());
                    break;
                case 2:
                    tree.ROOT = tree.delete(tree.ROOT, val);
                    System.out.println(tree.printTree());
                    break;
                case 3:
                    System.out.println(tree.rank(tree.ROOT, val));
                    break;
                case 4:
                    System.out.println(tree.find(tree.ROOT, val));
                    break;
                case 5:
                    System.out.println(tree.pre(tree.ROOT, val));
                    break;
                case 6:
                    System.out.println(tree.sub(tree.ROOT, val));
            }
        }
    }
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值