LeetCode探索(85):729-我的日程安排表 I

246 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

题目

实现一个 MyCalendar 类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。

当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订

日程可以用一对整数 startend 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end

实现 MyCalendar 类:

  • MyCalendar() 初始化日历对象。
  • boolean book(int start, int end) 如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true 。否则,返回 false 并且不要将该日程安排添加到日历中。

示例:

输入:
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
输出:
[null, true, false, true]

解释:
MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False ,这个日程安排不能添加到日历中,因为时间 15 已经被另一个日程安排预订了。
myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日历中,因为第一个日程安排预订的每个时间都小于 20 ,且不包含时间 20 。

提示:

  • 0 <= start < end <= 10^9
  • 每个测试用例,调用 book 方法的次数最多不超过 1000 次。

思考

本题难度中等。

首先是读懂题意。重复预订是指两个日程安排有一些时间上的交叉。我们可以在每次新加一个日程时,遍历是否与之前的日程存在交叉即可,也就是暴力解法。

此外,可以使用二分查找的方法解决。定义日程数组events,最小值为0,最大值为events.length - 1,通过二分查找,如果start < this.events[guess][1] && this.events[guess][0] < end,则重复预订了。否则继续二分查找,直至找到与日程[start, end]相邻的最小区间,并插入日程[start, end]即可。

解答

方法一:暴力法

var MyCalendar = function() {
  this.calendar = []
}
/** 
 * @param {number} start 
 * @param {number} end
 * @return {boolean}
 */
MyCalendar.prototype.book = function(start, end) {
  for (let item of this.calendar) {
    if (item[0] < end && start < item[1]) return false
  }
  this.calendar.push([start, end])
  return true
}

// 执行用时:188 ms, 在所有 JavaScript 提交中击败了36.96%的用户
// 内存消耗:49.9 MB, 在所有 JavaScript 提交中击败了57.97%的用户
// 通过测试用例:107 / 107

复杂度分析:

  • 时间复杂度:knO(k)=O(n2)\sum_k^n O(k) = O(n^2),其中 n 指的是日常安排的数量。
  • 空间复杂度:O(n)

方法二:二分查找

var MyCalendar = function() {
  this.events = [];
};
MyCalendar.prototype.book = function(start, end) {
  var min = 0;
  var max = this.events.length - 1;
  var guess;
  while (min <= max) {
    var guess = Math.floor((min + max) / 2);
    if (start < this.events[guess][1] && this.events[guess][0] < end) {
      return false;
    } else if (this.events[guess][1] > start) {
      min = guess + 1;
    } else {
      max = guess - 1; 
    }
  }
  // 在索引min处插入[start, end]
  this.events.splice(min, 0, [start, end]);
  return true;
};

// 执行用时:136 ms, 在所有 JavaScript 提交中击败了93.81%的用户
// 内存消耗:49.6 MB, 在所有 JavaScript 提交中击败了75.77%的用户
// 通过测试用例:107 / 107

复杂度分析:

  • 时间复杂度:O(nlogm),n 指的是日常安排的数量。二分查找需要执行 O(logm) 轮,需要进行 n 次二分查找。
  • 空间复杂度:O(n)

参考