面試高頻題:使用兩個棧實現一個特殊棧,要求 push、pop、min 三個操作的時間複雜度均為 O(1),其中 min() 返回當前棧中的最小值。
用一個變量 $minVal 保存當前最小值。push 時比較更新,min 直接返回。但 pop 彈出最小值時,需重新掃描整棧找新最小值,複雜度退化為 O(n)。
輔助棧維護遞增序列。min 取棧頂即可,但 push/pop 需插入/刪除排序位置,無法達到 O(1)。
核心洞察:輔助棧不存「所有元素排序」,而是存「每一步操作後的當前最小值」。
這樣三個操作均為單次棧頂訪問,嚴格 O(1)。
top === -1) {
$this->data[] = $element;
$this->minStack[] = $element;
$this->top++;
return true;
}
// 關鍵:比較新元素與當前最小值,取較小者入輔助棧
$currentMin = min($element, $this->minStack[$this->top]);
$this->data[] = $element;
$this->minStack[] = $currentMin;
$this->top++;
return true;
}
/** 出棧 */
public function pop(): int|bool {
if ($this->top === -1) return false;
array_pop($this->minStack);
$this->top--;
return array_pop($this->data);
}
/** 獲取最小值 */
public function min(): int|bool {
if ($this->top === -1) return false;
return $this->minStack[$this->top];
}
}
// 測試用例
$stack = new MinStack();
$stack->push(12);
$stack->push(56);
$stack->push(23);
$stack->push(89);
$stack->push(4);
var_dump($stack->min()); // int(4)
$stack->pop();
var_dump($stack->min()); // int(12)
$stack->push(8);
var_dump($stack->min()); // int(8)
| 操作 | 數據棧 (頂→底) | 輔助棧 (頂→底) | 說明 |
|---|---|---|---|
| push(12) | [12] | [12] | 首個元素,雙棧同步 |
| push(56) | [56, 12] | [12, 12] | 56 > 12,輔助棧複製當前最小值 |
| push(23) | [23, 56, 12] | [12, 12, 12] | 同理 |
| push(89) | [89, 23, 56, 12] | [12, 12, 12, 12] | 同理 |
| push(4) | [4, 89, 23, 56, 12] | [4, 12, 12, 12, 12] | 4 < 12,輔助棧更新為 4 |
| pop() | [89, 23, 56, 12] | [12, 12, 12, 12] | 彈出 4,輔助棧同步彈出,最小值恢復 12 |
| push(8) | [8, 89, 23, 56, 12] | [8, 12, 12, 12, 12] | 8 < 12,輔助棧記錄新最小值 8 |
min() 的實時系統、算法競賽、嵌入式受限環境max(),同理再加一個 maxStack。(value, count) 元組,節省空間。std::stack 配合 std::vector 實現同樣邏輯。這道題考察的不僅是棧的特性,更是「以空間換時間」及「狀態歷史記錄」的通用設計模式,值得深度體會。