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; }