111 年 國立政治大學資訊科學系碩士班《資料結構與演算法》
第 1 題10 分
Give the tight asymptotic bound of the following recurrence:
登入後即可作答並保存紀錄。
核心觀念
本題考查遞迴式的漸近分析,重點是判斷各層遞迴所產生的成本總和。
給定:
其中每次遞迴問題規模縮小為原來的 ,而當層額外成本為 。
解題方法:遞迴樹展開
逐層展開遞迴式:
第 層的非遞迴成本為:
當問題規模縮小至 時:
因此遞迴深度為:
所有非遞迴成本的總和為幾何級數:
其總和為:
由幾何級數公式:
可得:
由於幾何級數的和受到常數 限制,因此:
以 Master Theorem 驗證
標準形式為:
第 2 題25 分
Design a singly linked list that supports the following operations in C or pseudo code.
2.1 Initializing an empty linked list
2.2 Inserting a node of value , before the first element of the linked list
2.3 Inserting a node of value , before the -th element of the linked list
2.4 Delete the -th element of the linked list
2.5 Reverse the linked list in the most memory-efficient way.
登入後即可作答並保存紀錄。
核心觀念
本題考查單向鏈結串列(singly linked list)的基本操作,以及「以指標重新連結節點」的能力。
每個節點包含:
- 資料欄位
data - 指向下一個節點的指標
next
串列以指標 head 指向第一個節點:
本題以下採用 第 個元素為第一個元素 的編號方式。空串列以 head = NULL 表示。
資料結構定義
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct {
Node *head;
} LinkedList;
2.1 Initializing an empty linked list
解題方法
建立串列時,尚未有任何節點,因此只需將 head 設為 NULL。
void initialize(LinkedList *list) {
list->head = NULL;
}
複雜度
- 時間:
- 額外空間:
2.2 Inserting a node of value , before the first element
解題方法
新節點必須成為新的第一個節點,連結順序如下:
原本:
插入 後:
操作順序:
- 建立新節點
newNode - 將
newNode->next指向原本的head - 將
head改為newNode
void insertFirst(LinkedList *list, int x) {
Node *newNode = malloc(sizeof(Node));
if (newNode == NULL) {
return; // 記憶體配置失敗
}
newNode->data = x;
newNode->next = list->head;
list->head = newNode;
}
為何必須先設定 newNode->next
若先執行:
list->head = newNode;
再設定 newNode->next,原本的第一個節點位置可能遺失。因此必須先保存連結:
newNode->next = list->head;
list->head = newNode;
複雜度
- 時間:
- 額外空間:,不計新建立的節點本身
2.3 Inserting a node of value , before the -th element
位置定義
「插入第 個元素之前」代表:
- :插入串列最前端
- :找到第 個節點,將新節點接在其後
- :插入目前串列尾端
例如原本為:
在第 個元素前插入 :
解題方法
先處理 的特殊情況,因為此時需要修改 head。其餘情況使用指標走訪到第 個節點。
int insertBefore(LinkedList *list, int i, int x) {
if (i < 1) {
return 0; // 位置不合法
}
if (i == 1) {
insertFirst(list, x);
return 1;
}
Node *prev = list->head;
// 找到第 i-1 個節點
for (int pos = 1; pos < i - 1 && prev != NULL; pos++) {
prev = prev->next;
}
// 若 prev 不存在,表示 i 超出允許範圍
if (prev == NULL) {
return 0;
}
Node *newNode = malloc(sizeof(Node));
if (newNode == NULL) {
return 0;
}
newNode->data = x;
newNode->next = prev->next;
prev->next = newNode;
return 1;
}
指標連結重點
插入前:
插入後:
因此必須依序執行:
newNode->next = prev->next;
prev->next = newNode;
若先執行 prev->next = newNode,卻尚未保存原本的 target,便可能失去後半段串列。
複雜度
最壞情況需走訪至串列尾端:
- 時間:
- 額外空間:,不計新建立的節點
2.4 Delete the -th element
解題方法
刪除第 個節點時:
- :將
head移至第二個節點 - :找到第 個節點,將它直接連到第 個節點,再釋放第 個節點
原本:
刪除 後:
程式碼
第 3 題15 分
A stack can be implemented by a linked list. Design the following stack operations in C or pseudo code mainly based on the operations of Question 2.
3.1 Inserting (or 'push') an element at the top of the stack
3.2 Removing (or 'pop') an element at the top of the stack
3.3 Getting the value of the element at the top of the stack
登入後即可作答並保存紀錄。
核心觀念
本題考查以單向鏈結串列(singly linked list)實作堆疊(stack)。
堆疊遵守後進先出(Last In, First Out, LIFO):
push:將元素放入堆疊頂端。pop:移除並回傳堆疊頂端元素。top:取得堆疊頂端元素,但不移除。
以鏈結串列實作時,令指標 top 指向串列第一個節點:
因此,所有堆疊操作都在鏈結串列的頭端進行。鏈結串列頭端插入與刪除皆不需移動其他節點,時間複雜度為 。
資料結構定義
typedef struct Node {
int data;
struct Node *next;
} Node;
Node *top = NULL;
其中:
data儲存元素值。next指向下一個節點。top == NULL表示堆疊為空。
3.1 Push:將元素放入堆疊頂端
解題方法
新節點必須插入鏈結串列頭端,並將 top 更新為新節點。
操作順序如下:
- 配置一個新節點。
- 將資料存入新節點。
- 令新節點的
next指向原本的top。 - 將
top更新為新節點。
void push(int value) {
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Stack overflow\n");
return;
}
newNode->data = value;
newNode->next = top;
top = newNode;
}
假設原本堆疊為:
top -> A -> B -> NULL
執行 push(C) 後:
top -> C -> A -> B -> NULL
複雜度
- 時間複雜度:
- 額外空間複雜度:
- 新增節點所使用的總空間:每個元素一個節點,因此為
3.2 Pop:移除並取得堆疊頂端元素
解題方法
堆疊頂端就是鏈結串列的第一個節點,因此直接刪除頭節點即可。
操作順序如下:
- 檢查堆疊是否為空。
- 暫存目前的
top節點。 - 儲存該節點的資料。
- 將
top移至下一個節點。 - 釋放原本頂端節點的記憶體。
- 回傳被移除的元素。
int pop(int *result) {
if (top == NULL) {
printf("Stack underflow\n");
return 0;
}
Node *temp = top;
*result = temp->data;
top = top->next;
free(temp);
return 1;
}
使用方式:
int value;
if (pop(&value)) {
printf("Popped value = %d\n", value);
}
假設原本堆疊為:
top -> C -> A -> B -> NULL
執行 pop() 後:
- 回傳
C - 堆疊變為:
第 4 題10 分
Design Depth First Search algorithm, DFS-search(G, s), traversing a graph G from a source vertex s using the above stack operations.
登入後即可作答並保存紀錄。
核心觀念
深度優先搜尋(DFS)從來源頂點 出發,沿著尚未拜訪的鄰點一路深入;走到沒有未拜訪鄰點的頂點時,再回到上一層繼續搜尋。遞迴版 DFS 會用函式呼叫堆疊記錄搜尋路徑;本題要求用堆疊操作實作,便以明確的堆疊取代遞迴。
題目提到的「上述堆疊操作」未附在題幹中。以下假設可使用 push、pop 與 isEmpty,並以鄰接串列表示圖。為了讓堆疊版的拜訪順序與遞迴 DFS 一致,每個堆疊項目除了頂點,也記錄該頂點下一個要檢查的鄰點位置。
解題方法
令 Adj[v] 表示頂點 的鄰點串列,visited[v] 記錄是否拜訪過。堆疊中的項目 (v, i) 表示目前正在搜尋頂點 ,而 i 是下一個要檢查的鄰點索引。
第 5 題10 分
Design insert sort which sorts the following playing cards according to its number while the suit of card does not matter (不管花色).
[HINT: insert sort here is not stable.]
Input: club11, spade 10, club10, heart 7
Output: heart 7, club10, spade 10, club11
登入後即可作答並保存紀錄。
核心觀念
本題考查 Insertion Sort(插入排序法),並特別要求使用不穩定排序(not stable)。
- 排序鍵值:撲克牌的「數字」。
- 花色:不參與比較,因此
club 10與spade 10視為相同鍵值。 - 穩定排序:相同鍵值的元素,排序後仍維持原本相對順序。
- 不穩定排序:相同鍵值的元素,排序後可以改變原本相對順序。
原始資料中:
題目輸出卻是:
因此,題目要求相同數字的牌也可以交換順序,插入排序的比較條件必須讓相等元素發生移動。
解題方法
Insertion Sort 將陣列分成兩部分:
- 左側:已排序區域。
- 右側:尚未處理區域。
每次取出右側第一張牌作為 key,將左側大於或等於 key 的牌向右移動,再把 key 插入適當位置。
為了使排序不穩定,比較條件使用:
而不是穩定版本使用的:
當兩張牌數字相同時,A[j].number >= key.number 仍會成立,因此原本位於前面的相同數字牌會向右移動,造成花色順序改變。
關鍵演算法
InsertionSort(A):
for i = 1 to n - 1:
key = A[i]
j = i - 1
while j >= 0 and A[j].number >= key.number:
A[j + 1] = A[j]
j = j - 1
A[j + 1] = key
比較時只使用:
A[j].number
不使用:
A[j].suit
因此花色不會影響排序結果。
逐步執行
初始陣列為:
第一次插入:spade 10
已排序區域:
比較:
成立,因此將 club 11 向右移動:
插入 spade 10:
第二次插入:club 10
目前已排序區域為:
第 6 題10 分
Use Kruskal's algorithm to find the minimum spanning tree of the graph, G, with vertices and the adjust matrix A.
where is the weight of the edge between and .
登入後即可作答並保存紀錄。
核心觀念
本題考查最小生成樹(Minimum Spanning Tree, MST)與 Kruskal 演算法。
對含有 個頂點的連通無向圖而言,生成樹必須符合:
- 包含全部 個頂點;
- 不含任何環;
- 恰有 條邊。
最小生成樹則是在所有生成樹中,使邊權總和最小者。本題有 個頂點,因此最小生成樹必須包含:
條邊。
Kruskal 演算法的原則是:
- 將所有邊依權重由小到大排序;
- 依序選取邊;
- 若加入該邊不會形成環,便將其加入生成樹;
- 直到選出 條邊為止。
其正確性建立在最小生成樹的切割性質:對任意切割,跨越該切割的最小權重邊可安全地加入某棵最小生成樹。
解題方法
由鄰接矩陣取出上三角部分,即可得到每條無向邊:
依權重排序後,逐邊執行 Kruskal 演算法:
| 步驟 | 考慮的邊 | 權重 | 是否加入 | 理由 |
|---|---|---|---|---|
| 1 | 8 | 加入 | 不形成環 | |
| 2 | 9 | 加入 | 不形成環 |
第 7 題20 分
Input: a graph G(V, E)
Task: find the compact set C of G, C is a compact set if (the smallest external distance of C is still larger than the longest internal distance in C, i.e., the C of Question 6 is )
備註:
一、作答於試題上者, 不予計分。
二、試題請隨卷繳交。
登入後即可作答並保存紀錄。
核心觀念
本題考查加權圖中的「緊密集合(compact set)」判定。
對頂點集合 ,若滿足
則 為 compact set。
其中:
- 左側是集合 與外部頂點之間的最小距離,稱為最小外部距離。
- 右側是集合 內部頂點之間的最大距離,稱為最大內部距離。
- 核心條件是:集合內部最遠的兩點,仍然比集合與外部最近的兩點更接近。
因此,compact set 可視為圖中一個「內部緊密、外部隔離」的群集。
解題方法
對每一個候選集合 ,依序計算:
以及
最後檢查:
若成立,則 是 compact set;若不成立,則不是。
本題題幹未附上第 6 題的圖形或完整距離矩陣,因此無法重新列出各邊權重進行數值驗算。依題目括號中已給出的第 6 題距離關係,符合條件的集合為: