当 k 个日程安排有一些时间上的交叉时(例如 k 个日程安排都在同一时间内),就会产生 k 次预订。
给你一些日程安排 [start, end) ,请你在每个日程安排添加后,返回一个整数 k ,表示所有先前日程安排会产生的最大 k 次预订。
实现一个 MyCalendarThree 类来存放你的日程安排,你可以一直添加新的日程安排。
MyCalendarThree() 初始化对象。 
int book(int start, int end) 返回一个整数 k ,表示日历中存在的 k 次预订的最大值。 
示例:
**输入:**
["MyCalendarThree", "book", "book", "book", "book", "book", "book"]
[[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]]
**输出:**
[null, 1, 1, 2, 3, 3, 3]
**解释:**
MyCalendarThree myCalendarThree = new MyCalendarThree();
myCalendarThree.book(10, 20); // 返回 1 ,第一个日程安排可以预订并且不存在相交,所以最大 k 次预订是 1 次预订。
myCalendarThree.book(50, 60); // 返回 1 ,第二个日程安排可以预订并且不存在相交,所以最大 k 次预订是 1 次预订。
myCalendarThree.book(10, 40); // 返回 2 ,第三个日程安排 [10, 40) 与第一个日程安排相交,所以最大 k 次预订是 2 次预订。
myCalendarThree.book(5, 15); // 返回 3 ,剩下的日程安排的最大 k 次预订是 3 次预订。
myCalendarThree.book(5, 10); // 返回 3
myCalendarThree.book(25, 55); // 返回 3
提示:
0 <= start < end <= 109 
- 每个测试用例,调用 
book 函数最多不超过 400次 
方法一:差分数组
思路与算法
可以参考「731. 我的日程安排表 II 」的解法二,我们可以采用同样的思路即可。利用差分数组的思想,每当我们预定一个新的日程安排 [\textit{start}, \textit{end}),在 start 计数 cnt}[\textit{start}] 加 1,表示从 start 预定的数目加 1;从 end 计数 cnt}[\textit{end}] 减 1,表示从 end 开始预定的数目减 1。此时以起点 x 开始的预定的数目 book}x = \sum{y \le x}\textit{cnt}[y],我们对计数进行累加依次求出最大的预定数目即可。由于本题中 start}, \textit{end 数量较大,我们利用 TreeMap 计数即可。
代码
[sol1-Python3]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | from sortedcontainers import SortedDict
  class MyCalendarThree:     def __init__(self):         self.d = SortedDict()
      def book(self, start: int, end: int) -> int:         self.d[start] = self.d.setdefault(start, 0) + 1         self.d[end] = self.d.setdefault(end, 0) - 1
          ans = maxBook = 0         for freq in self.d.values():             maxBook += freq             ans = max(ans, maxBook)         return ans
   | 
  
[sol1-Java]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | class MyCalendarThree {     private TreeMap<Integer, Integer> cnt;
      public MyCalendarThree() {         cnt = new TreeMap<Integer, Integer>();     }          public int book(int start, int end) {         int ans = 0;         int maxBook = 0;         cnt.put(start, cnt.getOrDefault(start, 0) + 1);         cnt.put(end, cnt.getOrDefault(end, 0) - 1);         for (Map.Entry<Integer, Integer> entry : cnt.entrySet()) {             int freq = entry.getValue();             maxBook += freq;             ans = Math.max(maxBook, ans);         }         return ans;     } }
  | 
  
[sol1-C++]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | class MyCalendarThree { public:     MyCalendarThree() {              }          int book(int start, int end) {         int ans = 0;         int maxBook = 0;         cnt[start]++;         cnt[end]--;         for (auto &[_, freq] : cnt) {             maxBook += freq;             ans = max(maxBook, ans);         }         return ans;     } private:     map<int, int> cnt; };
  | 
  
[sol1-Golang]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | type MyCalendarThree struct {     *redblacktree.Tree }
  func Constructor() MyCalendarThree {     return MyCalendarThree{redblacktree.NewWithIntComparator()} }
  func (t MyCalendarThree) add(x, delta int) {     if val, ok := t.Get(x); ok {         delta += val.(int)     }     t.Put(x, delta) }
  func (t MyCalendarThree) Book(start, end int) (ans int) {     t.add(start, 1)     t.add(end, -1)
      maxBook := 0     for it := t.Iterator(); it.Next(); {         maxBook += it.Value().(int)         if maxBook > ans {             ans = maxBook         }     }     return }
  | 
  
复杂度分析
方法二:线段树
思路与算法
利用线段树,假设我们开辟了数组 arr}[0,\cdots, 10^9],初始时每个元素的值都为 0,对于每次行程预定的区间 [\textit{start}, \textit{end}) ,则我们将区间中的元素 arr}[\textit{start},\cdots,\textit{end}-1] 中的每个元素加 1,最终只需要求出数组 arr 的最大元素即可。实际我们不必实际开辟数组 arr,可采用动态线段树,懒标记 lazy 标记区间 [l,r] 进行累加的次数,tree 记录区间 [l,r] 的最大值,最终返回区间 [0,10^9] 中的最大元素即可。
代码
[sol2-Python3]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | class MyCalendarThree:     def __init__(self):         self.tree = defaultdict(int)         self.lazy = defaultdict(int)
      def update(self, start: int, end: int, l: int, r: int, idx: int):         if r < start or end < l:             return         if start <= l and r <= end:             self.tree[idx] += 1             self.lazy[idx] += 1         else:             mid = (l + r) // 2             self.update(start, end, l, mid, idx * 2)             self.update(start, end, mid + 1, r, idx * 2 + 1)             self.tree[idx] = self.lazy[idx] + max(self.tree[idx * 2], self.tree[idx * 2 + 1])
      def book(self, start: int, end: int) -> int:         self.update(start, end - 1, 0, 10 ** 9, 1)         return self.tree[1]
   | 
  
[sol2-Java]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | class MyCalendarThree {     private Map<Integer, Integer> tree;     private Map<Integer, Integer> lazy;
      public MyCalendarThree() {         tree = new HashMap<Integer, Integer>();         lazy = new HashMap<Integer, Integer>();     }          public int book(int start, int end) {         update(start, end - 1, 0, 1000000000, 1);         return tree.getOrDefault(1, 0);     }
      public void update(int start, int end, int l, int r, int idx) {         if (r < start || end < l) {             return;         }          if (start <= l && r <= end) {             tree.put(idx, tree.getOrDefault(idx, 0) + 1);             lazy.put(idx, lazy.getOrDefault(idx, 0) + 1);         } else {             int mid = (l + r) >> 1;             update(start, end, l, mid, 2 * idx);             update(start, end, mid + 1, r, 2 * idx + 1);             tree.put(idx, lazy.getOrDefault(idx, 0) + Math.max(tree.getOrDefault(2 * idx, 0), tree.getOrDefault(2 * idx + 1, 0)));         }     } }
  | 
  
[sol2-C++]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | class MyCalendarThree { public:     unordered_map<int, pair<int, int>> tree;
      MyCalendarThree() {
      }          void update(int start, int end, int l, int r, int idx) {         if (r < start || end < l) {             return;         }          if (start <= l && r <= end) {             tree[idx].first++;             tree[idx].second++;         } else {             int mid = (l + r) >> 1;             update(start, end, l, mid, 2 * idx);             update(start, end, mid + 1, r, 2 * idx + 1);             tree[idx].first = tree[idx].second + max(tree[2 * idx].first, tree[2 * idx + 1].first);         }     }
      int book(int start, int end) {                     update(start, end - 1, 0, 1e9, 1);         return tree[1].first;     } };
  | 
  
[sol2-C#]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | public class MyCalendarThree {     private Dictionary<int, int[]> tree;
      public MyCalendarThree() {         tree = new Dictionary<int, int[]>();     }          public int Book(int start, int end) {         Update(start, end - 1, 0, 1000000000, 1);         return tree[1][0];     }
      public void Update(int start, int end, int l, int r, int idx) {         if (r < start || end < l) {             return;         }          if (start <= l && r <= end) {             if (!tree.ContainsKey(idx)) {                 tree.Add(idx, new int[2]);             }             tree[idx][0]++;             tree[idx][1]++;         } else {             int mid = (l + r) >> 1;             Update(start, end, l, mid, 2 * idx);             Update(start, end, mid + 1, r, 2 * idx + 1);             if (!tree.ContainsKey(idx)) {                 tree.Add(idx, new int[2]);             }             if (!tree.ContainsKey(2 * idx)) {                 tree.Add(2 * idx, new int[2]);             }             if (!tree.ContainsKey(2 * idx + 1)) {                 tree.Add(2 * idx + 1, new int[2]);             }             tree[idx][0] = tree[idx][1] + Math.Max(tree[2 * idx][0], tree[2 * idx + 1][0]);         }     } }
  | 
  
[sol2-C]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
   | #define MAX(a, b) ((a) > (b) ? (a) : (b))
  typedef struct HashItem {     int key;     int maxBook;     int lazy;     UT_hash_handle hh; } HashItem;
  typedef struct {     HashItem *tree; } MyCalendarThree;
 
  MyCalendarThree* myCalendarThreeCreate() {     MyCalendarThree *obj = (MyCalendarThree *)malloc(sizeof(MyCalendarThree));     obj->tree = NULL;     return obj; }
  void update(MyCalendarThree* obj, int start, int end, int l, int r, int idx) {     if (r < start || end < l) {         return;     }      if (start <= l && r <= end) {         HashItem *pEntry = NULL;         HASH_FIND_INT(obj->tree, &idx, pEntry);         if (pEntry == NULL) {             pEntry = (HashItem *)malloc(sizeof(HashItem));             pEntry->key = idx;             pEntry->maxBook = 1;             pEntry->lazy = 1;             HASH_ADD_INT(obj->tree, key, pEntry);         } else {             pEntry->maxBook++;             pEntry->lazy++;         }     } else {         int mid = (l + r) >> 1;         update(obj, start, end, l, mid, 2 * idx);         update(obj, start, end, mid + 1, r, 2 * idx + 1);         int lchid = idx * 2, rchid = idx * 2 + 1;         int lmax = 0, rmax = 0;         HashItem *pEntry1 = NULL, *pEntry2 = NULL;         HASH_FIND_INT(obj->tree, &lchid, pEntry1);         HASH_FIND_INT(obj->tree, &rchid, pEntry2);         if (pEntry1) {             lmax = pEntry1->maxBook;         }         if (pEntry2) {             rmax = pEntry2->maxBook;         }         HashItem *pEntry = NULL;         HASH_FIND_INT(obj->tree, &idx, pEntry);         if (pEntry) {             pEntry->maxBook = pEntry->lazy + MAX(lmax, rmax);         } else {             pEntry = (HashItem *)malloc(sizeof(HashItem));             pEntry->key = idx;             pEntry->maxBook = 1;             pEntry->lazy = 0;             HASH_ADD_INT(obj->tree, key, pEntry);         }     } }
  int myCalendarThreeBook(MyCalendarThree* obj, int start, int end) {     update(obj, start, end - 1, 0, 1e9, 1);     int idx = 1;     HashItem *pEntry = NULL;     HASH_FIND_INT(obj->tree, &idx, pEntry);     if (pEntry) {         return pEntry->maxBook;     }     return 0; }
  void myCalendarThreeFree(MyCalendarThree* obj) {     struct HashItem *curr,*tmp;     HASH_ITER(hh, obj->tree, curr, tmp) {         HASH_DEL(obj->tree, curr);          free(curr);                  }      free(obj);  }
   | 
  
[sol2-Golang]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
   | type pair struct{ num, lazy int }
  type MyCalendarThree map[int]pair
  func Constructor() MyCalendarThree {     return MyCalendarThree{} }
  func (t MyCalendarThree) update(start, end, l, r, idx int) {     if r < start || end < l {         return     }     if start <= l && r <= end {         p := t[idx]         p.num++         p.lazy++         t[idx] = p     } else {         mid := (l + r) / 2         t.update(start, end, l, mid, idx*2)         t.update(start, end, mid+1, r, idx*2+1)         p := t[idx]         p.num = p.lazy + max(t[idx*2].num, t[idx*2+1].num)         t[idx] = p     } }
  func (t MyCalendarThree) Book(start, end int) int {     t.update(start, end-1, 0, 1e9, 1)     return t[1].num }
  func max(a, b int) int {     if b > a {         return b     }     return a }
  | 
  
[sol2-JavaScript]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | var MyCalendarThree = function() {     this.tree = new Map();     this.lazy = new Map(); };
  MyCalendarThree.prototype.book = function(start, end) {     this.update(start, end - 1, 0, 1000000000, 1);     return this.tree.get(1) || 0; };
  MyCalendarThree.prototype.update = function(start, end, l, r, idx) {     if (r < start || end < l) {         return;     }      if (start <= l && r <= end) {         this.tree.set(idx, (this.tree.get(idx) || 0) + 1);         this.lazy.set(idx, (this.lazy.get(idx) || 0) + 1);     } else {         const mid = (l + r) >> 1;         this.update(start, end, l, mid, 2 * idx);         this.update(start, end, mid + 1, r, 2 * idx + 1);         this.tree.set(idx, (this.lazy.get(idx) || 0) + Math.max((this.tree.get(2 * idx) || 0), (this.tree.get(2 * idx + 1) || 0)));     } }
  | 
  
复杂度分析
时间复杂度:O(n \log C),其中 n 为日程安排的数量。由于使用了线段树查询,线段树的最大深度为 \log C,每次最多会查询 \log C 个节点,每次求最大的预定需的时间复杂度为 O(\log C + \log C),因此时间复杂度为 O(n \log C),在此 C 取固定值即为 10^9。
 
空间复杂度:O(n \log C),其中 n 为日程安排的数量。由于该解法采用的为动态线段树,线段树的最大深度为 \log C,每次预定最多会在线段树上增加 \log C 个节点,因此空间复杂度为 O(n \log C),在此 C 取固定值即为 10^9。