111 高普考高考三級 資訊處理 資料結構 考古題詳解
第 一 題25 分
以下是一中序運算式(Infix expression)轉換(Convert)成後序運算式(Postfix expression)的演算法:
operstk = the empty stack;
while (not end of input) { symb = next input character;
if (symb is an operand) add symb to the postfix string;
else { while (!empty(operstk) && precedence(stacktop(operstk), symb)) { topsymb = pop(operstk); add topsymb to the postfix string; } /* end while */
if (empty(operstk) || symb != ')') push(operstk, symb); else topsymb = pop(operstk); } /* end else / } / end while */
while (!empty(operstk)) { topsymb = pop(operstk); add topsymb to the postfix string; } /* end while */
其中資料結構:
- 「operstk」:用來儲存運算子的堆疊(Stack)。
- 「stacktop(operstk)」:表示 top 指標所指堆疊 operstk 的運算子。
程序(Procedures)或函數(Functions):
- 「empty(operstk)」:檢查堆疊 operstk 是否為空的布林函數。
- 「pop(operstk)」:從堆疊 operstk 中取出一運算子。
- 「push(operstk, symb)」:將運算子 symb 存入堆疊 operstk。
- 「precedence(op₁,op₂)」:布林函數,定義在一沒有左右括弧的中序運算式中, 運算子出現在 運算子的左邊時,當 運算子優先順序不低於 運算子,則設定成 TRUE,否則為 FALSE。例如:
- precedence('*', '+') = TRUE
- precedence('+', '+') = TRUE
- precedence('+', '*') = FALSE
為了處理運算式左右括弧,設定下列的 precedence:
- precedence('(', op) = FALSE /* op 為任一運算子 */
- precedence(op, '(') = FALSE /* op 為除 ')' 外的任一運算子 */
- precedence(op, ')') = TRUE /* op 為除 '(' 外的任一運算子 */
- precedence(')', op) = undefined /* op 為任一運算子 */
以中序運算式 為例,執行上述演算法,依處理每一個運算子或運算元時,輸出 postfix string 及 operstk 內容為何(「eos」表示 end of string)?
| symbol | postfix string | operstk |
|---|---|---|
| ( | ||
| 2 | ||
| + | ||
| 3 | ||
| ) | ||
| * | ||
| 4 | ||
| eos |
登入後即可作答並保存紀錄。
核心觀念
本題考查資料結構中「堆疊(Stack)應用:中序運算式(Infix)轉後序運算式(Postfix)」的經典演算法追蹤(Trace)。
- 基本轉換原理:
- 運算元(Operand):不改變相對順序,讀取後直接輸出至後序運算式字串(postfix string)。
- 運算子(Operator):依照優先順序(Precedence)與結合性(Associativity)暫存於堆疊(operstk)中。當新讀入運算子的優先權低於或等於堆疊頂端運算子時,堆疊頂端運算子必須先彈出(pop)輸出,以確保優先運算者先出現。
- 括弧處理機制:
- 演算法透過自訂布林函數
precedence(op1, op2)規範運算子間的輸出關係。 - 左括弧
(:進入堆疊後扮演「隔離邊界」角色。依題目定義precedence('(', op) = FALSE,表示左括弧位於堆疊內時,任何後續運算子皆無法將其彈出,直接推入堆疊。 - 右括弧
):代表該子運算式結束。依題目定義precedence(op, ')') = TRUE(除(外),會強制將堆疊中累積至左括弧為止的所有運算子依序彈出輸出;當碰觸到左括弧時(precedence('(', ')') = FALSE),迴圈中止,演算法透過else topsymb = pop(operstk);將對應的左括弧彈出消除,且右括弧本身不推入堆疊,達成「後序運算式不含括弧」的特性。
- 終止處理(End of String, eos):
- 讀取完整運算式後,將堆疊內殘留的所有運算子由頂端至底端全數彈出並輸出。
解題方法
給定中序運算式為:。
堆疊 operstk 初始狀態為空堆疊,以下逐步追蹤各輸入字元之執行動作:
- 讀入
symb = '(':
symb為運算子。operstk目前為空,不執行彈出迴圈。- 滿足
empty(operstk) || symb != ')',執行push(operstk, '(')。 - 狀態:
postfix string為空;operstk為(。
- 讀入
symb = '2':
symb為運算元,直接加入後序字串。- 狀態:
postfix string為2;operstk為(。
- 讀入
symb = '+':
symb為運算子。- 檢查
precedence(stacktop(operstk), '+'),即precedence('(', '+') = FALSE,不執行彈出迴圈。 - 滿足
empty(operstk) || symb != ')',執行push(operstk, '+')。 - 狀態:
postfix string為2;operstk為( +(頂端為+)。
- 讀入
symb = '3':
symb為運算元,直接加入後序字串。- 狀態:
postfix string為23;operstk為( +。
第 二 題25 分
利用鏈結串列(Linked list)實做佇列(Queues)。給予如下鏈結串列節點及佇列定義,front 指標指在串列第一個節點,rear 指標指在串列最後一個節點,請使用 C 語言完成 insert(pq, x) 程序,將整數值 加入(Insert)到佇列。程式需檢查佇列加入前是否為空的鏈結串列,可使用函數 getnode() 配置(Allocate)一新節點。
struct node { int info; struct node *next; };
typedef struct node *NODEPTR;
struct queue { NODEPTR front, rear; };
struct queue q;
NODEPTR getnode() { NODEPTR p;
p = (NODEPTR)malloc(sizeof(struct node)); return(p); }
insert(pq, x)
struct queue *pq; int x; { NODEPTR p; }
登入後即可作答並保存紀錄。
核心觀念
- 佇列(Queue)之操作特性: 佇列為先進先出(FIFO, First-In-First-Out)的線性資料結構。所有新增操作(Enqueue / Insert)皆在隊尾(Rear / Back)進行;刪除操作(Dequeue / Remove)則在隊首(Front)進行。
- 鏈結串列(Linked List)指標維護: 在不具備虛擬頭節點(Dummy Head)的單向鏈結佇列中:
front指向佇列的第一個節點。rear指向佇列的最後一個節點。- 當佇列為空時,
front == NULL且rear == NULL。
- K&R C 函式宣告風格: 本題採用經典教科書(如 Tenenbaum 等所著之《Data Structures Using C》)常見的 K&R C 語法格式定義函式參數,解題時遵循題目的變數與結構定義實作函式內容即可。
解題方法
將整數 加入佇列的程序可分為四個關鍵步驟:
- 配置新節點並初始化欄位:
呼叫題目提供的
getnode函式取得一塊新的節點記憶體,並令指標變數p指向它。
- 資料欄位賦值:
p->info = x; - 指標欄位初始化:因為新節點即將成為佇列的最後一個節點,其後續無其他節點,故必須設定
p->next = NULL;。
- 檢查佇列加入前是否為空:
依題意需檢查串列加入前是否為空。在合法的佇列狀態下,佇列為空等價於
pq->front == NULL(或pq->rear == NULL)。 - 分流處理串列串接:
- 狀況一:佇列原為空(
pq->front == NULL或pq->rear == NULL): 此時新節點p不僅是隊尾,也是隊首,因此必須將pq->front指向p。 - 狀況二:佇列原非空:
原佇列最後一個節點為
pq->rear,必須將原最後節點的next指標接上新節點,即執行pq->rear->next = p;。
- 更新隊尾指標:
無論原佇列是否為空,新節點加入後皆成為佇列的最後一個節點,因此最後必須將
pq->rear更新指向新節點p(pq->rear = p;)。
解題技巧
第 三 題25 分
一個二元搜尋樹(Binary search tree)的前序追蹤(Preorder traversal)結果如下:
請建構此二元搜尋樹。接著利用如下 C 語言對二元樹節點的宣告,使用 C 語言寫一遞迴程式 sortTree(NODEPTR tree),輸入二元樹的根節點,來處理此二元樹的節點資料,並將資料依由小至大輸出。
struct node { int info; struct node *left; struct node *right; };
typedef struct node *NODEPTR;
void sortTree(NODEPTR tree) { }
登入後即可作答並保存紀錄。
核心觀念
- 二元搜尋樹(Binary Search Tree, BST)之定義: 對於樹中任意節點 :
- 若其左子樹不為空,則左子樹上所有節點的值皆小於節點 的值。
- 若其右子樹不為空,則右子樹上所有節點的值皆大於節點 的值。
- 其左、右子樹亦分別為二元搜尋樹。
- 走訪(Traversal)特性:
- 前序追蹤(Preorder Traversal):走訪順序為「根節點 左子樹 右子樹」。序列之首個元素即為樹(或子樹)之根節點。
- 中序追蹤(Inorder Traversal):走訪順序為「左子樹 根節點 右子樹」。任何二元搜尋樹經中序追蹤輸出之結果,必然為由小至大的嚴格遞增排序序列。
- 前序序列重建 BST 之唯一性: 一般二元樹僅給前序序列無法唯一決定樹形,但 BST 具備鍵值大小之排序約束。只要依前序序列確定根節點後,序列中凡小於根節點者必屬左子樹、大於根節點者必屬右子樹,故單靠前序序列即可唯一決定樹形。
解題方法
一、建構二元搜尋樹(分治法推導)
給定前序追蹤序列:
- 確定樹根(Root)與左右子樹劃分:
- 序列首位為 ,故整棵樹之根節點為 。
- 比 小之連續數值為左子樹前序序列:。
- 比 大之連續數值為右子樹前序序列:。
- 遞迴建構左子樹(以 之左子樹為例):
- 前序序列 之首位為 ,故 為 之左子節點。
- 比 小者為左子樹:,故 為 之左子節點(葉節點)。
- 比 大者為右子樹前序序列:。
- 首位為 ,故 為 之右子節點。
- 比 小者為左子樹前序序列:,右子樹為空。
- 首位為 ,故 為 之左子節點。
- 比 小者為左子樹:,故 為 之左子節點(葉節點),右子樹為空。
- 遞迴建構右子樹(以 之右子樹為例):
- 前序序列 之首位為 ,故 為 之右子節點。
- 比 小者無元素,故 之左子樹為空。
第 四 題25 分
用 表示一個無方向性圖形,其中 是點的集合, 是一組節點(Vertices)形成邊及對應權重(Weights)所組成的集合。今有一圖形 ,其中:
圖形的邊與權重值如下的定義儲存對應連接矩陣(Adjacency matrix)表示中的值:
#define MAX_EDGES 100
typedef struct { int col; int row; int weight; } edge;
edge a[MAX_EDGES];
已知陣列 儲存對應連接矩陣相連接邊的內容如下:
請畫出陣列 所儲存的圖形,然後,利用 Prim 演算法從節點 開始依加入其他節點的順序,畫出此圖之最小擴張樹(Minimum spanning tree),並計算其最低權重或成本值。
登入後即可作答並保存紀錄。
核心觀念
- 圖形之邊集合表示法(Edge Array / Coordinate List):
本題定義結構型態
edge包含(col, row, weight),以陣列 儲存無向圖 之各條邊及其權重值。無向邊連接點col與row,其連接關係具對稱性,即邊 與 視為同一條邊。 - 最小擴張樹(Minimum Spanning Tree, MST): 給定一個連通且具有權重的無向圖 ,其最小擴張樹為包含圖中所有頂點 的無環連通子圖(樹),且所有樹邊之權重總和為最小。若 ,則擴張樹恰有 條邊。本題頂點數 ,故擴張樹必由 5 條邊構成。
- Prim 演算法(Prim's Algorithm): 屬於貪婪演算法(Greedy Algorithm)。運作機制如下:
- 令集合 為已加入擴張樹之頂點集合,初始時 。
- 在每一步驟中,自跨越割(Cut)的邊(即一端在 、另一端在 的候選邊)中,挑選權重最小的邊 ,將頂點 納入 。
- 重複上述步驟,直到所有頂點皆加入 (即 )為止。
解題方法
步驟一:繪製陣列 所儲存之圖形
根據題目陣列 的資料,整理圖形的頂點與邊:
- 頂點集合:
- 邊與權重集合(共 9 條邊):
- 權重
- 權重
- 權重
- 權重
- 權重
- 權重
- 權重
- 權重
- 權重
此圖形由頂點群體 與 構成,兩者由邊 、、 相互連接。圖形 結構繪製如下:
(20)
[0] ================================ [5]
/ \ /|
/ \ (1) (24) / |
(2)/ \ / |
/ [4] ---------- (10) --------- [2] | (25)
/ / / | |
/ (3) / (15) / | |
[3] ---+ / | |
| / | |
+----------------------------------+ |(7)
| |
[1]+
步驟二:執行 Prim 演算法(由節點 0 開始)
起始設定:,,最小擴張樹邊集合 。
| 步驟 | 目前樹頂點集合 | 未加入頂點 | 跨越割候選邊及權重 | 選取之邊 | 權重 | 新加入節點 |
|---|---|---|---|---|---|---|
| 初始 | , , | — | — | |||
| 1 | , , | |||||
| 2 | , , , | |||||
| 3 | , , | |||||
| 4 | , , |