Hard草稿★★★★★O(n log n) 時間 · O(n) 空間
ArrayBinary SearchDivide and ConquerMerge SortBIT
Patterns🎯 二分搜尋✂️ 分治
尚未複習過

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

315Count of Smaller Numbers After SelfBinary SearchHardBinary Search

給定整數陣列 nums,回傳陣列 counts,其中 counts[i] = nums[i] 右側比它小的元素個數。

Example:

Input: nums = [5,2,6,1] Output: [2,1,1,0] (5 右側有 {2,1} 兩個更小;2 右側 {1};6 右側 {1};1 右側無)

Intuition

TIP

核心思路:這是「計算逆序對」的變形。用歸併排序,在合併兩段時,當左段元素被放入結果,就累加「此刻已從右段放入的元素數」——那些都是比它小且在它右邊的。

Approaches

⭐ Merge Sort Counting — O(n log n) / O(n)
  • Idea: 在 merge 階段,左段元素放入時累加已放入的右段數
  • Time: O(n log n) - 歸併
  • Space: O(n) - 索引與暫存
class Solution {
    fun countSmaller(nums: IntArray): List<Int> {
        val n = nums.size
        val counts = IntArray(n)
        val indices = IntArray(n) { it }   // 追蹤原始索引
        mergeSort(nums, indices, counts, 0, n - 1)
        return counts.toList()
    }

    private fun mergeSort(nums: IntArray, idx: IntArray, counts: IntArray, lo: Int, hi: Int) {
        if (lo >= hi) return
        val mid = (lo + hi) ushr 1
        mergeSort(nums, idx, counts, lo, mid)
        mergeSort(nums, idx, counts, mid + 1, hi)

        val merged = IntArray(hi - lo + 1)
        var i = lo; var j = mid + 1; var k = 0
        var rightCount = 0
        while (i <= mid && j <= hi) {
            if (nums[idx[j]] < nums[idx[i]]) {
                rightCount++                 // 右段這個更小者先放
                merged[k++] = idx[j++]
            } else {
                counts[idx[i]] += rightCount // 左段元素:右邊已放的都更小
                merged[k++] = idx[i++]
            }
        }
        while (i <= mid) { counts[idx[i]] += rightCount; merged[k++] = idx[i++] }
        while (j <= hi) merged[k++] = idx[j++]
        for (t in merged.indices) idx[lo + t] = merged[t]
    }
}
Note: Fenwick Tree (BIT) Approach

把數值離散化(座標壓縮)後,從右往左掃描:對每個 nums[i],查詢 BIT 中「比它小的值」的累計出現次數(即右側更小數),再把 nums[i] 加入 BIT。同樣 O(n log n),但常數較小、也較容易擴充成各種區間統計。

🔑 Takeaways