497. 非重叠矩形中的随机点

127 阅读1分钟

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

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

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

实现 Solution 类:

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

算法:
方法一:水塘抽样
分两步走:

  1. 按矩形面积占总面积的比例选择一个矩形。
    遍历矩形,设cur为当前矩形的点数(注意2*2的矩形有9个点),sum为已遍历的所有矩形的面积,rand(sum) < cur则意味着选择当前矩形,否则维持选择的矩形不变。
  2. 从矩形中随机选择一个点返回
    随机的点为[a + rand(x - a), b + rand(y - b)]注意点的总数为边长+1之积。 时间复杂度O(n),空间复杂度O(1)
import "math/rand"
type Solution struct {
    Rects [][]int
}


func Constructor(rects [][]int) Solution {
    return Solution{Rects:rects}
}


func (this *Solution) Pick() []int {
    cur := 0
    sum := 0
    index := 0
    for i, rec := range this.Rects {
        cur = (rec[2] - rec[0] + 1)*(rec[3] - rec[1] + 1)
        sum = sum + cur
        if rand.Intn(sum) < cur {
            index = i
        }
    }
    // rand.Intn(n)的取值范围为[0,n),所以最终结果为[a + rand(x - a + 1), b + rand(y - b + 1)]
    return []int{this.Rects[index][0] + rand.Intn(this.Rects[index][2] - this.Rects[index][0] + 1), this.Rects[index][1] + rand.Intn(this.Rects[index][3] - this.Rects[index][1] + 1)}
}

方法二:前缀和+二分查找
方法一每次pick都需要遍历所有矩形,我们不如在初始化时就将所有矩形的点数(权重)计算好,然后根据前置前缀和取一个随机数,判断随机数位于哪个矩阵,在从矩阵中随机去一个点即可。 点数前缀和:
[0, 9, 21, 58],如果点数[0-9]则选择第一个矩阵

import "math/rand"
type Solution struct {
    Rects [][]int
    PointSum []int
}


func Constructor(rects [][]int) Solution {
    s := Solution{
        Rects: rects,
        PointSum: make([]int, len(rects) + 1), 
    }
    for i, rec := range rects {
        s.PointSum[i + 1] = s.PointSum[i] + (rec[2] - rec[0] + 1)*(rec[3] - rec[1] + 1)
    }
    return s
}


func (this *Solution) Pick() []int {
    // 注意rand.Ints的范围
    num := rand.Intn(this.PointSum[len(this.PointSum) - 1] + 1)
    mid := 0
    // 找到num所在PointSum的下界
    left, right := 0, len(this.Rects) - 1
    for left < right {
        mid = (left + right + 1) >> 1
        if this.PointSum[mid] < num {
            left = mid 
        } else {
            right = mid - 1
        }
    }
    index := left 
    return []int{this.Rects[index][0] + rand.Intn(this.Rects[index][2] - this.Rects[index][0] + 1), this.Rects[index][1] + rand.Intn(this.Rects[index][3] - this.Rects[index][1] + 1)}
}