LeetCode探索(95):497-非重叠矩形中的随机点

100 阅读2分钟

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

题目

给定一个由非重叠的轴对齐矩形的数组 rects ,其中 rects[i] = [ai, bi, xi, yi] 表示 (ai, bi) 是第 i 个矩形的左下角点,(xi, yi) 是第 i 个矩形的右上角角点。设计一个算法来随机挑选一个被某一矩形覆盖的整数点。矩形周长上的点也算做是被矩形覆盖。所有满足要求的点必须等概率被返回。

在一个给定的矩形覆盖的空间内任何整数点都有可能被返回。

请注意 ,整数点是具有整数坐标的点。

实现 Solution 类:

  • Solution(int[][] rects) 用给定的矩形数组 rects 初始化对象。
  • int[] pick() 返回一个随机的整数点 [u, v] 在给定的矩形所覆盖的空间内。

示例 1:

img

输入: 
["Solution","pick","pick","pick","pick","pick"]
[[[[-2,-2,-1,-1],[1,0,3,0]]],[],[],[],[],[]]
输出: 
[null,[-1,-2],[2,0],[-2,-1],[3,0],[-2,-2]

解释:
Solution solution = new Solution([[-2, -2, 1, 1], [2, 2, 4, 6]]);
solution.pick(); // 返回 [1, -2]
solution.pick(); // 返回 [1, -1]
solution.pick(); // 返回 [-1, -2]
solution.pick(); // 返回 [-2, -2]
solution.pick(); // 返回 [0, 0]

提示:

  • 1 <= rects.length <= 100
  • rects[i].length == 4
  • -109 <= ai < xi <= 109
  • -109 <= bi < yi <= 109
  • xi - ai <= 2000
  • yi - bi <= 2000
  • 所有的矩形不重叠。
  • pick 最多被调用 104 次。

解答

方法一:前缀和 + 二分查找

/**
 * @param {number[][]} rects
 */
var Solution = function(rects) {
  // this.arr是覆盖的整数点的前缀和
  this.arr = [0]
  this.rects = rects
  for (const rect of rects) {
    const a = rect[0], b = rect[1], x = rect[2], y = rect[3]
    this.arr.push(this.arr[this.arr.length - 1] + (x - a + 1) * (y - b + 1))
  }
  // console.log('this.arr', this.arr)
}
/**
 * @return {number[]}
 */
Solution.prototype.pick = function() {
  // 整数k是在覆盖的所有整数点的编号中等概率随机取值得到的
  let k = Math.floor(Math.random() * this.arr[this.arr.length - 1])
  // 确定整数点k位于哪个矩形中
  const rectIndex = binarySearch(this.arr, k + 1) - 1
  // 求出当前矩形中有几个整数点
  k -= this.arr[rectIndex]
  // 确定整数点k在矩形中的位置
  const rect = this.rects[rectIndex]
  const a = rect[0], b = rect[1], y = rect[3]
  // 一列有几个点
  const col = y - b + 1
  // 处于第几列
  const da = Math.floor(k / col)
  const db = k - col * da
  return [a + da, b + db]
}
// 二分查找
const binarySearch = (arr, target) => {
  let low = 0, high = arr.length - 1
  while (low <= high) {
    const mid = Math.floor((high - low) / 2) + low
    const num = arr[mid]
    if (num === target) {
      return mid
    } else if (num > target) {
      high = mid - 1
    } else {
      low = mid + 1
    }
  }
  return low
}

// let solution = new Solution([[-2, -2, 1, 1], [2, 2, 4, 6]]) // this.arr [0, 16, 31]
// solution.pick() // 返回 [1, -2]
// solution.pick() // 返回 [1, -1]
// solution.pick() // 返回 [-1, -2]
// solution.pick() // 返回 [-2, -2]
// solution.pick() // 返回 [0, 0]

复杂度分析:

  • 时间复杂度:构造函数复杂度为 O(n),pick 函数复杂度为 O(logn),其中 n 为 rects 的长度。
  • 空间复杂度:构造函数复杂度为 O(n),pick 函数复杂度为 O(1),其中 n 为 rects 的长度。

参考