LeetCode 每日 1 题:考场就座

104 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 31 天,点击查看活动详情

考场就座

原题地址

在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 。

当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,那么学生就坐在 0 号座位上。)

返回 ExamRoom(int N) 类,它有两个公开的函数:其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。

示例:

输入:["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
输出:[null,0,9,4,2,null,5]
解释:
ExamRoom(10) -> null
seat() -> 0,没有人在考场里,那么学生坐在 0 号座位上。
seat() -> 9,学生最后坐在 9 号座位上。
seat() -> 4,学生最后坐在 4 号座位上。
seat() -> 2,学生最后坐在 2 号座位上。
leave(4) -> null
seat() -> 5,学生最后坐在 5 号座位上。

提示:

  • 1<=N<=1091 <= N <= 10^9
  • 在所有的测试样例中 ExamRoom.seat() 和 ExamRoom.leave() 最多被调用 10410^4 次。
  • 保证在调用 ExamRoom.leave(p) 时有学生正坐在座位 p 上。

思路分析

  • 首先定义构造函数中的属性,使用数组来存储 student,然后max表示最大序号为 n - 1;
  • 在学生离场的时候使用二分法进行查找删除,同样,也可以使用数组的 findIndex 方法,找到对应的下标进行删除,但是二分法的耗时更短;
  • 学生入场时,需要按照以下几个步骤来判断完成:
    • 如果考场中为空,则直接坐在 0 号座位上;
    • 如果考场中只有一个学生时,则判断这个学生离考场中首尾的距离,寻找选一点的位置就坐;
    • 如果考场中有多个学生时,则需要判断每两个学生之间的距离,然后找到最远距离,坐在其中间。

AC 代码

/**
 * @param {number} n
 */
var ExamRoom = function(n) {
  this.student = []
  this.max = n - 1
};

/**
 * @return {number}
 */
ExamRoom.prototype.seat = function() {
  const student = this.student

  if (!student.length) {
    student.push(0)
    return 0
  }

  if (student.length === 1) {
    const val = student[0]
    if (val > (this.max - val)) {
      this.student.unshift(0)
      return 0
    } else {
      this.student.push(this.max);
      return this.max
    }
  }

  let result = -1
  let maxSpace = 0
  let value
  let index
  for (let i = 0; i < student.length; i++) {
    let current = student[i]
    if (current - result < 1) {
      continue
    }
    let half = result + ((current - result) >> 1)
    let space = Math.min(half - result, current - half)
    if (space > maxSpace) {
      maxSpace = space
      value = half
      index = i
    }

    if (i === 0 && current !== 0 && current > space) {
      maxSpace = current
      value = 0
      index = i
    }

    result = current
  }

  if (value === undefined && student[student.length - 1] !== this.max) {
    index = student.length
    value = this.max
  }

  this.student.splice(index, 0, value)
  return value
};

/** 
 * @param {number} p
 * @return {void}
 */
ExamRoom.prototype.leave = function(p) {
  let left = 0
  let right = this.student.length - 1
  while (left <= right) {
    const mid = left + ((right - left) >> 1)
    if (this.student[mid] === p) {
      this.student.splice(mid, 1)
      break
    } else if (this.student[mid] > p) {
      right = mid - 1
    } else if (this.student[mid] < p) {
      left = mid + 1
    }
  }
};

/**
 * Your ExamRoom object will be instantiated and called as such:
 * var obj = new ExamRoom(n)
 * var param_1 = obj.seat()
 * obj.leave(p)
 */

结果:

  • 执行结果: 通过
  • 执行用时:248 ms, 在所有 JavaScript 提交中击败了82.76%的用户
  • 内存消耗:48.5 MB, 在所有 JavaScript 提交中击败了96.55%的用户
  • 通过测试用例:126 / 126

END