Medium草稿★★★★★O(n) 時間 · O(n) 空間
ArrayBinary SearchPrefix SumMathDesign
Patterns🏗️ 資料結構設計➕ 前綴和🎯 二分搜尋🧮 數學・組合
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

528Random Pick with WeightBinary SearchMediumBinary Search

給定權重陣列 w,實作 pickIndex():以正比於 w[i] 的機率隨機回傳索引 i。例如 w = [1,3] 時,回傳 0 的機率 1/4、回傳 1 的機率 3/4。

Example:

Input: w = [1,3],多次呼叫 pickIndex() Output: 0 約 25%、1 約 75%

Intuition

TIP

核心思路:把權重變成前綴和,等於在數線上切出長度正比於權重的區段。隨機取 [1, total] 一個整數,用二分找它落在哪個區段(第一個 prefix[i] ≥ target)。

Approaches

⭐ Prefix Sum + Binary Search — build O(n), query O(log n) / O(n)
  • Idea: 前綴和把加權抽樣化為「在累積區間上二分定位」
  • Time: 建構 O(n)、每次 pickIndex O(log n)
  • Space: O(n) - 前綴和陣列
import kotlin.random.Random

class Solution(w: IntArray) {
    private val prefix = IntArray(w.size)
    private val total: Int

    init {
        var sum = 0
        for (i in w.indices) {
            sum += w[i]
            prefix[i] = sum
        }
        total = sum
    }

    fun pickIndex(): Int {
        val target = Random.nextInt(total) + 1   // 1..total
        // lower bound:第一個 prefix[i] >= target
        var lo = 0; var hi = prefix.size - 1
        while (lo < hi) {
            val mid = (lo + hi) ushr 1
            if (prefix[mid] < target) lo = mid + 1 else hi = mid
        }
        return lo
    }
}
Note: Why [1, total] and lower bound

target ∈ [1, total](含端點、不含 0)能讓每個索引的命中數正好等於其權重:索引 i 的命中區間是 prefix[i-1] < target ≤ prefix[i],長度恰為 w[i]。用 lower bound(第一個 ≥ target)即可定位到該區間的右端點索引 i

🔑 Takeaways