Medium草稿★★★★★O(1) 時間 · O(n) 空間
StackMonotonic StackDesign
Patterns📚 單調堆疊🏗️ 資料結構設計🥞 堆疊
尚未複習過

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

901Online Stock SpanStackMediumStack

設計一個類別 StockSpanner,收集每日股票價格並回傳該日股票價格的跨度 (span)。跨度定義為從今天開始往回數,股價連續小於等於今天價格的天數(包含今天)。

Example:

Input: [“StockSpanner”, “next”, “next”, “next”, “next”, “next”, “next”, “next”], [[], [100], [80], [60], [70], [60], [75], [85]] Output: [null, 1, 1, 1, 2, 1, 4, 6]

Intuition

TIP

核心思路:用遞減單調堆疊,存 (價格, 跨度) 配對,遇到新價格時吸收所有小於等於它的棧頂跨度。

Approaches

1. Brute Force: Store Prices, Scan Back — O(n) per call / O(n)
  • Idea: 把每天價格存進清單,每次 next 從尾端往回數連續 ≤ 當前價的天數
  • Time: 每次 next 最差 O(n),總計 O(n²)
  • Space: O(n) - 儲存所有價格
class StockSpanner() {
    private val prices = ArrayList<Int>()

    fun next(price: Int): Int {
        prices.add(price)
        var span = 0
        var i = prices.size - 1
        while (i >= 0 && prices[i] <= price) { span++; i-- }
        return span
    }
}
⭐ 2. Monotonic Decreasing Stack — amortized O(1) / O(n)
  • Idea: 堆疊維護嚴格遞減的價格序列,每個元素記錄 (價格, 跨度)。新價格進來時,把所有 <= 它的元素彈出,累加跨度
  • Time: 每次 next 均攤 O(1),總計 O(n)
  • Space: O(n)
class StockSpanner() {
    // Pair: (price, span)
    private val stack = ArrayDeque<Pair<Int, Int>>()

    fun next(price: Int): Int {
        var span = 1

        while (stack.isNotEmpty() && stack.last().first <= price) {
            span += stack.removeLast().second
        }

        stack.addLast(price to span)
        return span
    }
}

🔑 Takeaways