🧱 U7 資料結構・知識點 K19

堆疊與佇列

模擬堆疊和佇列的放入、取出,並用在生活情境

B7-01-StackQueue
練習平台
點標題可以展開或收合

1學習目標

  • 知道堆疊是「後進先出」、佇列是「先進先出」。
  • 會用清單模擬堆疊:從最後放入、從最後拿出。
  • 會用清單模擬佇列:從最後放入、從最前面拿出。
  • 知道拿資料之前要先確認不是空的。

2概念說明

資料放進去、拿出來的順序,會決定程式的行為。最常見的兩種規則:

堆疊(Stack)佇列(Queue)
生活例子一疊盤子、瀏覽器的上一頁排隊買東西、叫號系統
規則後進先出:最後放上去的最先拿走先進先出:最先來的最先服務
放入放在清單最後放在清單最後
拿出拿走清單最後一筆拿走清單第一筆
10
20
30
10
20
30
左:堆疊,要拿走的是最上面的 30|右:佇列,要拿走的是最前面的 10
你想做的事BlocklyScratch
放入自清單 堆疊 添加 最後一筆 為(數字)添加(數字)到 堆疊
堆疊拿出自清單 堆疊 移除 最後一筆刪除 堆疊 的第(清單 堆疊 的長度)項
佇列拿出自清單 佇列 移除 第一筆刪除 佇列 的第 1 項
是不是空的長度 堆疊 = 0清單 堆疊 的長度 = 0
⚠️ 空的時候不能拿

堆疊或佇列已經空了還要拿資料,程式可能會出錯。題目通常會說「如果是空的就跳過」,所以拿之前要先判斷「長度 > 0」。

3示範題:跟著一步一步做(2 題)

先讀題,再一步一步點開來看。建議每看完一步,就先自己在平台上試著寫寫看。

示範題 1

堆疊操作模擬

L2|進階M1-11-01
請你模擬堆疊(後進先出)的操作:指令代碼1代表PUSH(把數字加入堆疊頂端),指令代碼2代表POP(把堆疊頂端的數字移除;如果堆疊是空的,這個指令就跳過不執行)。全部指令執行完後,請輸出堆疊裡剩下的內容,由最底部到最頂部依序輸出,空白分隔;如果堆疊是空的,輸出「空」。 第一行輸入M,代表共有M個指令 第二行輸入M個指令代碼(1或2) 第三行輸入M個數字,只有指令代碼是1(PUSH)時才會用到對應位置的數字,指令代碼是2時該位置數字請忽略。
輸入
M、M個指令代碼、M個數字
輸出
堆疊剩餘內容(底到頂)或「空」
範例1
輸入
5
1 1 2 1 2
10 20 0 30 0
輸出
10
第 1 步:讀題,找出輸入和輸出
  • 輸入:三行。第一行 M(有幾個指令),第二行 M 個指令代碼(1 是放入、2 是拿出),第三行 M 個數字(代碼是 1 時才用到)。
  • 輸出:堆疊裡剩下的數字(從底到頂);空的就輸出「空」。
第 2 步:先把輸入讀進兩個清單

第 i 個指令要搭配第 i 個數字,所以先把兩行分別讀進「指令清單」和「數字清單」(K18 的平行清單):

詢問並等待 → M
重複 M 次:讀入 指令,添加到 指令清單
重複 M 次:讀入 數值,添加到 數字清單
第 3 步:一個一個執行指令
堆疊 設為 空清單
i 從 1 數到 M:
    如果 指令清單 的第 i 項 = 1:
        把 數字清單 的第 i 項 添加到 堆疊 最後
    否則:
        如果 堆疊 的長度 > 0:
            移除 堆疊 的最後一筆
如果 堆疊 的長度 = 0:說出 空
否則:依序說出 堆疊 的每一項
第 4 步:用範例走一次

指令「1 1 2 1 2」、數字「10 20 0 30 0」:

i指令動作堆疊
11放入 1010
21放入 2010 20
32拿走最後的 2010
41放入 3010 30
52拿走最後的 3010

輸出「10」。

第 5 步:對照範例答案

按「載入範例」,看看「移除」外面有一層「如果不是空的」的判斷。

示範題 2

佇列操作模擬

L2|進階M1-11-02
請你模擬佇列(先進先出)的操作:指令代碼1代表ENQUEUE(把數字加入佇列最後面),指令代碼2代表DEQUEUE(把佇列最前面的數字移除;如果佇列是空的,這個指令就跳過不執行)。全部指令執行完後,請輸出佇列裡剩下的內容,由最前面到最後面依序輸出,空白分隔;如果佇列是空的,輸出「空」。 第一行輸入M,代表共有M個指令 第二行輸入M個指令代碼(1或2) 第三行輸入M個數字,只有指令代碼是1(ENQUEUE)時才會用到對應位置的數字。
輸入
M、M個指令代碼、M個數字
輸出
佇列剩餘內容(前到後)或「空」
範例1
輸入
5
1 1 2 1 2
10 20 0 30 0
輸出
30
第 1 步:和堆疊差在哪裡?

輸入格式和上一題完全一樣,程式也幾乎一樣,只差拿出的位置:佇列要拿走第一筆(最早進來的)。

    否則:
        如果 佇列 的長度 > 0:
            移除 佇列 的第一筆
第 2 步:用範例走一次

同樣的指令「1 1 2 1 2」、數字「10 20 0 30 0」:

i指令動作佇列
11放入 1010
21放入 2010 20
32拿走最前面的 1020
41放入 3020 30
52拿走最前面的 2030

輸出「30」。同一組指令,堆疊剩 10、佇列剩 30,這就是兩種規則的差別。

⚠️ 常見錯誤
  • 拿錯位置:堆疊拿最後一筆、佇列拿第一筆,弄反了答案就會對調。
  • 忘了判斷空的:題目測資通常會故意在空的時候下「拿出」指令。

4練習題(2 題)

照順序完成這些題目。寫完之後,可以在平台上按「載入範例」和自己的寫法比較。

瀏覽器上一頁模擬

L2|進階M1-11-04
這題多了什麼:瀏覽紀錄就是堆疊:造訪新頁面就放入,按上一頁就拿走最後一筆。和示範的差別只在最後:題目要的是「目前所在的頁面」,也就是堆疊的最後一筆;如果堆疊是空的,輸出 0。
看題目內容
請你模擬瀏覽器的『上一頁』功能:指令代碼1代表造訪一個新頁面(頁面代碼是一個數字,會被放進瀏覽紀錄堆疊);指令代碼2代表按下上一頁(從堆疊移除目前頁面,回到上一頁;如果已經沒有上一頁可以回,這個指令就跳過不執行)。全部指令執行完後,輸出目前所在頁面的代碼;如果從頭到尾都沒有造訪任何頁面(堆疊是空的),輸出0。 第一行輸入M,代表共有M個指令 第二行輸入M個指令代碼(1代表造訪,2代表上一頁) 第三行輸入M個數字,只有指令代碼是1時才會用到對應位置的數字。
輸入
M、M個指令代碼、M個數字
輸出
目前所在頁面代碼,若無則輸出0
範例1
輸入
4
1 1 1 2
100 200 300 0
輸出
200
💡 需要提示嗎?
瀏覽紀錄就是一個堆疊,目前所在的頁面就是堆疊最上面那一項。

排隊叫號系統模擬

L2|進階M1-11-05
這題多了什麼:排隊叫號就是佇列:新顧客放到最後,叫號時拿走第一筆。程式和示範 M1-11-02 幾乎一模一樣,試著自己從頭寫一次。
看題目內容
請你模擬排隊叫號系統:指令代碼1代表有一位顧客加入隊伍排隊(號碼是一個數字,加到隊伍最後面);指令代碼2代表叫號服務隊伍最前面的顧客(把他從隊伍移除;如果隊伍是空的,這個指令就跳過不執行)。全部指令執行完後,輸出還在排隊的顧客號碼,依隊伍前到後順序輸出,空白分隔;如果隊伍是空的,輸出「空」。 第一行輸入M,代表共有M個指令 第二行輸入M個指令代碼(1代表加入排隊,2代表叫號) 第三行輸入M個數字,只有指令代碼是1時才會用到對應位置的數字。
輸入
M、M個指令代碼、M個數字
輸出
還在排隊的顧客號碼(前到後)或「空」
範例1
輸入
4
1 1 1 2
101 102 103 0
輸出
102 103
💡 需要提示嗎?
排隊隊伍就是一個佇列:新顧客加在最後,叫號時服務最前面的人。

5延伸挑戰(1 題)

核心題都會了嗎?這些題目需要把好幾個技巧組合起來,想挑戰的話可以試試看。

🚀 括號配對是否合法 L3|挑戰
挑戰重點:括號配對可以想成堆疊:遇到「(」就放進去,遇到「)」就拿走一個配對。因為堆疊裡只會放「(」,其實只要一個計數變數「深度」就夠了:「(」深度加 1,「)」深度減 1。如果遇到「)」時深度已經是 0(沒有可以配對的),或是全部看完深度不是 0(還有沒配對的),就是不合法。
請你判斷一串只包含小括號的字串,括號是否完全合法配對(每個左括號都有對應的右括號,且順序正確,右括號不會在對應的左括號之前出現)。 第一行輸入一個只包含'('與')'的字串 如果合法配對,輸出「合法」;否則輸出「不合法」。
輸入
括號字串
輸出
合法 或 不合法
範例1
輸入
(())
輸出
合法
範例2
輸入
(()
輸出
不合法
💡 需要提示嗎?
遇到左括號就記錄一個「還沒配對」,遇到右括號就配掉一個;如果中途配不到,或最後還有剩,就不合法。

6自我檢核

學完之後,檢查看看你能不能做到這些事。有做不到的,就回到上面對應的地方再看一次。

  • 我能用生活例子說明堆疊和佇列的差別。
  • 我會用清單模擬堆疊:放在最後、從最後拿。
  • 我會用清單模擬佇列:放在最後、從最前面拿。
  • 我會在拿資料之前先判斷是不是空的。