題目描述:給定陣列 prices,其中 prices[i] 代表第 i 件商品的原價。若在第 i 件之後存在第一個 j(j > i)使得 prices[j] <= prices[i],則第 i 件商品以 prices[i] - prices[j] 的折後價售出;否則以原價售出。回傳每件商品的最終售價陣列。
解題思路:
此題的核心是:對每個位置 i,找到其右側第一個小於或等於 prices[i] 的值,這正是「Next Smaller or Equal Element」問題,可用單調棧高效解決。
維護一個單調遞增棧(存索引)。從左到右遍歷,當遇到 prices[i] 時:
prices[i] <= prices[棧頂],說明棧頂元素找到了它的折扣來源:折後價 = prices[棧頂] - prices[i]。更新答案後彈出棧頂,重複此步驟。i 推入棧,等待右側出現更小或等於的價格。遍歷完成後,棧中剩餘的索引沒有折扣,對應答案保持原價。
相比暴力的雙重迴圈 O(n²),單調棧解法每個元素最多入棧出棧各一次,達到 O(n)。
時間複雜度:O(n) — 每個索引最多被推入棧一次、彈出棧一次,整體為線性時間。
空間複雜度:O(n) — 棧最多容納所有 n 個索引(當價格嚴格遞增時),加上輸出陣列 O(n)。
1. 初始化 ans = prices 的副本(預設無折扣),空棧 stk(存索引)
2. 從 i = 0 到 n-1 遍歷:
a. 當棧非空且 prices[棧頂] >= prices[i] 時:
- 令 top = 棧頂索引,彈出棧頂
- ans[top] = prices[top] - prices[i](套用折扣)
b. 將 i 推入棧
3. 棧中剩餘元素無折扣,ans 對應位置保持原價
4. 回傳 ans對每個 i,線性掃描 j = i+1 到 n-1,找到第一個 prices[j] <= prices[i] 後套用折扣並 break。邏輯最直觀,對本題的資料規模(n ≤ 500)也能通過,但不具擴展性。
for (int i = 0; i < n; i++) {
ans[i] = prices[i];
for (int j = i + 1; j < n; j++) {
if (prices[j] <= prices[i]) {
ans[i] = prices[i] - prices[j];
break;
}
}
}
從右到左遍歷,維護單調遞增棧(存價格值而非索引)。對每個位置 i,先彈出棧中所有嚴格大於 prices[i] 的值,此時若棧非空,棧頂就是右側第一個 ≤ prices[i] 的值。然後計算折扣並將 prices[i] 推入棧。這種寫法和從左到右邏輯對稱,但需要在彈出後查看棧頂,稍微複雜一些。
Next Smaller Element 的嚴格性:本題的條件是 prices[j] <= prices[i](允許等於)。若條件改為嚴格小於 prices[j] < prices[i],算法需要如何調整?比較兩種情況在 while 條件中的差異。
多次折扣:若規則改為「後方所有價格 ≤ prices[i] 的商品都給折扣,折扣取其中最大值」,應如何修改?提示:這時需要找「右側最大的 ≤ prices[i] 的值」,是否還能用單調棧高效解決?
本題與 LC 739 的關聯:LC 739(Daily Temperatures)求每個溫度等待多少天才能出現更高溫度,是「Next Greater Element」的距離版本;本題是「Next Smaller or Equal」的值版本。請說明這兩題在棧操作條件上的核心差異,並思考如果本題要求回傳的是「折扣金額(prices[j] 的值)而非折後價」,程式碼需要改哪裡?