[LeetCode] 729. My Calendar I 731. My Calendar II 732. My Calendar III 题解

Posted Fanny123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode] 729. My Calendar I 731. My Calendar II 732. My Calendar III 题解相关的知识,希望对你有一定的参考价值。

题目描述

MyCalendar主要实现一个功能就是插入指定起始结束时间的事件,对于重合的次数有要求。

  1. MyCalendar I要求任意两个事件不能有重叠的部分,如果插入这个事件会导致重合,则插入失败,不进行插入;否则插入并返回true。
  2. My Calendar II要求任意三个事件不能有重叠的部分,但是两个事件可以有重叠。同样是成功返回true,失败返回false。
  3. My Calendar III没有要求,对于每次插入新事件,求当前总体最大的重叠事件的个数。

MyCalendar I

对于不能重合的事件,可以利用BST二叉搜索树,每个节点代表一个事件区间,如果要插入的部分全部在当前节点的左侧或者右侧,则左递归或者右递归,否则,插入失败。
如果是用循环实现,则需要保存插入节点的父节点以及是父节点的左子还是右子。循环实现的代码如下:

    class Node {//节点有起始结束时间和左右子节点
        public Node(int start, int end) {
            l = start;
            r = end;
        }

        int l, r;
        Node left, right;
    }

    Node root = null;

    public boolean book(int start, int end) {
        if (root == null) {
            root = new Node(start, end);
        } else {
            Node cur = root;
            Node pre = null;//父节点
            boolean leftTag = false;//记录该插入的节点是左子还是右子
            while (cur != null) {
                pre = cur;
                if (end <= cur.l) {//应该在当前节点的左侧,往左子递归
                    leftTag = true;
                    cur = cur.left;
                } else if (start >= cur.r) {//应该在当前节点的右侧,往右子递归
                    leftTag = false;
                    cur = cur.right;
                } else {// 有重叠,不应该插入,返回false
                    return false;
                }
            }
            if (leftTag) {//根据tag确定是父亲的左子还是右子
                pre.left = new Node(start, end);
            } else {
                pre.right = new Node(start, end);
            }
        }
        return true;
    }

My Calendar II

用TreeMap保存所有事件开始及终止的位置以及它们的次数,

    TreeMap<Integer,Integer> treeMap;
    public MyCalendarTwo() {
        treeMap=new TreeMap<>();
    }
    
    public boolean book(int start, int end) {
        int a=treeMap.getOrDefault(start,0);
        int b=treeMap.getOrDefault(end,0);
        treeMap.put(start,a+1);
        treeMap.put(end,b-1);
        int count=0;

        for (Integer val : treeMap.values()) { 
            count+=val;//记录当前已开始但未结束的事件个数
            if(count>2){//如果事件个数>2,则说明有三个或者以上的重叠,不满足条件,要取消刚刚的插入
                if(a==0){//如果插入前的个数为0则可以直接删除这条记录,否则对次数进行更改
                    treeMap.remove(start);
                }else{
                     treeMap.put(start,a);
                }
                if(b==0){
                    treeMap.remove(end);
                }else{
                    treeMap.put(end,b);
                }
                return false;
            }
        }
        return true;
    }

My Calendar III

和第一题一样使用BST,没有重叠的区间的节点操作类似第一题,但是对于有重叠区间的节点,要进行分裂,把lser,lsre,slre,sler四种情况总结起来就是中间两个值作为当前节点的起始和终止时间,且次数要增加,两侧分别进行左递归和右递归,次数根据lr还是se再外侧来决定。【selr分别为待插入的start,end,当前节点的left和right】
注意,次数不能简单的为1,对于分裂了lr的情况(如lser和lsre、sler),递归的时候次数可能要指定为当前节点的已有次数,而这个不是固定为1的。所以插入次数也要作为参数进行传递。

    class Node {//节点有起始终止事件,左右子节点,这个时间区间的重叠次数
        int left, right;
        int count;
        Node leftChild, rightChild;

        public Node(int l, int r, int c) {
            left = l;
            right = r;
            count = c;
        }
    }

    int maxK = 1;//只要调用1次book,则最大记录至少为1,所以可以直接初始化为1
    Node root;

    public int book(int start, int end) {
        root = insert(root, start, end, 1);
        return maxK;
    }

    private Node insert(Node root2, int start, int end, int c) {//由于需要修改节点的链接关系,所以需要返回节点
        if (start >= end) {// no need to take action
            return root2;
        }
        if (root2 == null) {
            root2 = new Node(start, end, c);
            return root2;
        }
        int l = root2.left;
        int r = root2.right;
        if (end <= l) {//一定落在当前节点的左侧即左子树上,进行左递归
            root2.leftChild = insert(root2.leftChild, start, end, c);
        } else if (start >= r) {
            root2.rightChild = insert(root2.rightChild, start, end, c);
        } else {
            int[] a = new int[4];//给四个值排序
            if (start <= l) {
                a[0] = start;
                a[1] = l;
            } else {
                a[0] = l;
                a[1] = start;
            }
            if (end <= r) {
                a[2] = end;
                a[3] = r;
            } else {
                a[2] = r;
                a[3] = end;
            }
            root2.left = a[1];//中间的两个值作为当前节点的值
            root2.right = a[2];

            root2.leftChild = insert(root2.leftChild, a[0], a[1], start <= l ? c : root2.count);//左递归,如果start在外侧,则次数为c;如果l在外侧,则次数为当前节点的次数
            root2.rightChild = insert(root2.rightChild, a[2], a[3], end >= r ? c : root2.count);
            root2.count += c;//当前节点的次数要增加,并且根据大小情况选择性的更新maxK
            maxK = Math.max(maxK, root2.count);
        }
        return root2;
    }


以上是关于[LeetCode] 729. My Calendar I 731. My Calendar II 732. My Calendar III 题解的主要内容,如果未能解决你的问题,请参考以下文章

729. My Calendar I

[LC] 729. My Calendar I

FreeSWITCH折腾笔记5——G729转码支持

FreeSWITCH之配置G729转码

[LeetCode] My Calendar I

LeetCode 731. My Calendar II