MyCalendar主要实现一个功能就是插入指定起始结束时间的事件,对于重合的次数有要求。java
对于不能重合的事件,能够利用BST二叉搜索树,每一个节点表明一个事件区间,若是要插入的部分所有在当前节点的左侧或者右侧,则左递归或者右递归,不然,插入失败。
若是是用循环实现,则须要保存插入节点的父节点以及是父节点的左子仍是右子。循环实现的代码以下:code
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; }
用TreeMap保存全部事件开始及终止的位置以及它们的次数,<start,次数(正)>,和<end,次数(负数)>。要插入这个事件的实现过程是:先插入这个事件,再检测这个事件若是会致使>2个的区间有重合,则又取消插入,返回false,不然返回true。检测的方法是:遍历treemap中的entry(TreeMap是有序的),cnt+=entry.getValue()记录当前时刻开始了还没结束的事件个数。排序
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; }
和第一题同样使用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; }