題組內容

五、在計算機科學中,「stack」是 1 種常見的資料結構,請回答下列問題:(3 題,共 15 分)

(二)請以 2 個實際應用案例具體說明如何使用「stack」?(6 分)

詳解 (共 2 筆)

匿名鸚鵡
匿名鸚鵡
詳解 #7581554
2026/10/13

堆疊(Stack)具備後進先出(LIFO, Last-In-First-Out)的核心特性,在系統底層、編譯器設計與應用程式開發中有極為廣泛的應用。

1. 程式執行與記憶體管理:函式呼叫堆疊(Call Stack)

作業系統與程式語言執行環境(Runtime)用堆疊追蹤函式的呼叫與返回狀態:

  • 運作機制:呼叫函式時,將區域變數、傳入參數與返回位址(Return Address)打包成堆疊框架(Stack Frame / Activation Record)並推入(Push);函式執行完畢後彈出(Pop),CPU 依返回位址回到呼叫點繼續執行。

  • 典型現象:無窮遞迴或深層呼叫導致記憶體耗盡時,引發的 Stack Overflow(堆疊溢位)。

2. 編輯器與軟體系統:復原與重做(Undo / Redo)

文字編輯器(VS Code、Word)與繪圖軟體(Photoshop、Figma)普遍使用雙堆疊模型管理操作歷史:

  • 復原堆疊(Undo Stack):使用者執行新增、修改等操作時,狀態或逆向操作指令依序推入該堆疊。

  • 重做堆疊(Redo Stack):按下 Undo 時,從 Undo Stack 彈出最新狀態,並推入 Redo Stack;按下 Redo 則反向操作。若產生新編輯操作,Redo Stack 立即清空。

3. 網頁瀏覽器:歷史紀錄上一頁與下一頁(Back / Forward)

與 Undo/Redo 架構類似,瀏覽器維護兩個堆疊管理導航流程:

  • 返回堆疊:記錄已拜訪的網址,點選「上一頁」時彈出目前頁面並移至前一頁。

  • 前進堆疊:存放被退回的頁面,供「下一頁」快速前往。若在歷史狀態下點入新連結,前進堆疊會被清空。

4. 編譯器與語法解析(Parsing)

編譯器與直譯器在處理文字、符號與運算式時深度依賴堆疊:

  • 括號匹配與語法平衡:檢查程式碼中的 ()、[]、{} 是否成對合法。遇左括號推入,遇右括號則彈出比對型態是否相符。

  • 運算式轉換(Infix 轉 Postfix):如 Dijkstra 提出的 調車場演算法(Shunting-yard Algorithm),利用堆疊暫存運算子,依運算子優先權調整輸出順序。

  • 逆波蘭表示法求值(Postfix Evaluation):遇運算元入疊,遇運算子彈出兩個運算元計算後再入疊,是計算機與虛擬機(如 JVM、PostScript、Forth)的核心運算基礎。

5. 圖形與樹狀演算法:深度優先搜尋(DFS)與回溯法(Backtracking)

  • 深度優先搜尋(DFS):除了遞迴實作,使用顯式堆疊(Explicit Stack)可避免深層遞迴造成的呼叫堆疊溢位。

  • 回溯法應用:走迷宮演算法、八皇后問題、數獨求解等,將探索路徑的節點推入堆疊,若遇到死路則彈出退回上一節點繼續搜尋。

6. 標記語言解析(HTML / XML DOM 建構)

瀏覽器解析 HTML 或後端解析 XML 檔案時,遇起始標籤(如 <div>)推入堆疊,遇結束標籤(如 </div>)檢查是否與頂端標籤匹配並彈出,進而一步步建構出樹狀結構(DOM Tree)。

應用情境

堆疊存放內容

Push 時機

Pop 時機

函式呼叫(Call Stack)

Stack Frame(區域變數、返回位址)

呼叫函式

函式 return

Undo / Redo

操作差異(Command Pattern)或狀態快照

執行新動作

按下 Ctrl+Z

瀏覽器上一頁

網頁 URL / 頁面狀態

進入新網頁

點擊返回鍵

括號匹配

左括號符號及位置

遇到開符號

遇到閉符號

運算式求值

數值運算元 / 中間運算結果

讀取到數字 / 計算產出值

讀取到運算子需消耗運算元

hchungw
hchungw
詳解 #6126970
2024/06/11

(二) 使用「stack」的兩個實際應用案例

案例 1:表達式求值

在計算機科學中,堆疊常用於計算算術表達式,特別是中綴(infix)、前綴(prefix)和後綴(postfix)表達式的轉換和求值。這裡以後綴表達式求值為例。

後綴表達式求值:

  1. 後綴表達式是一種表示方式,操作符在操作數之後。例如,中綴表達式 3 + 4 * 2 / (1 - 5) 可以表示為後綴表達式 3 4 2 * 1 5 - / +。
  2. 求值方法:
    • 從左到右掃描後綴表達式。
    • 遇到操作數,將其壓入堆疊。
    • 遇到操作符,彈出堆疊頂端的兩個操作數,進行相應的運算,並將結果壓回堆疊。
    • 最終堆疊中剩下的唯一一個數即為表達式的值。

示例: 假設有後綴表達式 3 4 2 * 1 5 - / +,求值過程如下:

  • 初始:堆疊空。
  • 讀到 3,壓入堆疊:[3]
  • 讀到 4,壓入堆疊:[3, 4]
  • 讀到 2,壓入堆疊:[3, 4, 2]
  • 讀到 *,彈出 2 和 4,計算 4 * 2 = 8,壓入堆疊:[3, 8]
  • 讀到 1,壓入堆疊:[3, 8, 1]
  • 讀到 5,壓入堆疊:[3, 8, 1, 5]
  • 讀到 -,彈出 5 和 1,計算 1 - 5 = -4,壓入堆疊:[3, 8, -4]
  • 讀到 /,彈出 -4 和 8,計算 8 / -4 = -2,壓入堆疊:[3, -2]
  • 讀到 +,彈出 -2 和 3,計算 3 + -2 = 1,壓入堆疊:[1]
  • 最終結果:堆疊中剩下的數字 1 即為表達式的值。

案例 2:括號匹配

括號匹配是另一個使用堆疊的典型應用,用於檢查程式碼中括號是否正確配對,特別是在編譯器和語法分析器中。

括號匹配:

  1. 算法描述:
    • 從左到右掃描字符串。
    • 遇到開括號((, [, {),將其壓入堆疊。
    • 遇到閉括號(), ], }),檢查堆疊頂端是否為對應的開括號:
      • 若是,彈出堆疊頂端的開括號,繼續掃描。
      • 若否或堆疊為空,則括號不匹配。
    • 最終檢查堆疊是否為空,若是,則所有括號匹配;若否,則括號不匹配。

示例: 假設有字符串 "{[()()]}",檢查括號匹配過程如下:

  • 初始:堆疊空。
  • 讀到 {,壓入堆疊:[{]
  • 讀到 [,壓入堆疊:[{, []
  • 讀到 (,壓入堆疊:[{, [, (]
  • 讀到 ),彈出堆疊頂端 (,匹配,堆疊:[{, []
  • 讀到 (,壓入堆疊:[{, [, (]
  • 讀到 ),彈出堆疊頂端 (,匹配,堆疊:[{, []
  • 讀到 ],彈出堆疊頂端 [,匹配,堆疊:[{]
  • 讀到 },彈出堆疊頂端 {,匹配,堆疊空。
  • 結束:堆疊空,所有括號匹配。

這兩個案例展示了堆疊在處理表達式求值和括號匹配中的應用,說明了堆疊在管理後進先出(LIFO)資料上的便利性和有效性。