111 年 國立中正大學資訊管理學系碩士班乙組《資料結構》
第 1 題6 分
If n is 6, then the variable space requirement of this program is? (6 pt)
float rsum(float list[], int n) {
if (n) return rsum(list, n-1) + list[n-1];
return 0;
}
登入後即可作答並保存紀錄。
核心觀念
本題考遞迴函式的「變動空間需求」,重點是計算遞迴呼叫期間同時存在的啟動記錄(activation records)數量。
每次呼叫 rsum 需保存:
list:陣列位址,計 1 個 wordn:整數參數,計 1 個 word- return address:遞迴返回位址,計 1 個 word
因此每個啟動記錄需要 個 word。list[] 傳遞的是位址,不會在每次遞迴時複製整個陣列。
解題方法
當初始值為 時,呼叫順序為:
因此共有:
個啟動記錄。rsum(0) 尚未返回前,這 7 層都同時存在,形成最大堆疊空間。
第 2 題6 分
Calculate the total steps of the following function (6 pt)
void add( int a[][ ], int b[ ][ ], int c[ ][ ], int m, int n) {
for (int i = 0; i < n; i++)
for (int j=0; j < m; j++)
c[i][j] = a[i][j] +b[i][j];
}
登入後即可作答並保存紀錄。
核心觀念
此函式進行兩個 矩陣的逐元素相加:
外層索引 遍歷 列,內層索引 遍歷每列的 個元素,因此:
共有 次;對每個 , 共有 次。因此核心敘述執行:
次。
解題方法與總步數
以下採常見的基本運算計數方式:初始化、條件判斷、遞增、加法與指定各計 1 步,陣列存取視為常數時間。
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
c[i][j] = a[i][j] + b[i][j];
| 操作 | 執行次數 | 說明 |
|---|---|---|
i = 0 | 外層迴圈初始化 | |
i < n | 包含最後一次失敗判斷 | |
i++ | 外層迴圈遞增 | |
j = 0 | 每次外層迴圈重新初始化 | |
j < m | 每列包含最後一次失敗判斷 | |
j++ | 內層迴圈遞增 | |
a[i][j] + b[i][j] | 每個元素進行一次加法 | |
c[i][j] = \cdots | 每個元素進行一次指定 |
所以總步數為:
第 3 題12 分
Please transform the expressions from postfix to prefix. (12 pt)
(A) ab/c-de*+ac*-
(B) abc+*d/g-
登入後即可作答並保存紀錄。
核心觀念
- Postfix(後序表示法):運算子寫在兩個運算元之後,例如
ab+表示 。 - Prefix(前序表示法):運算子寫在兩個運算元之前,例如 的 prefix 為
+ab。 - 本題所有運算子皆視為二元運算子。
由 postfix 轉 prefix 時,從左至右掃描:
- 遇到運算元,推入堆疊。
- 遇到運算子,先取出堆疊頂端作為右運算元 ,再取下一個作為左運算元 。
- 組合成「運算子+左運算元+右運算元」,再推回堆疊:
其中 為目前的運算子。
(A)ab/c-de*+ac*-
逐步轉換
| 掃描符號 | 動作 | 堆疊內容(由底至頂) |
|---|---|---|
a | 推入 a | a |
b | 推入 b | a, b |
/ | 組合 /ab | /ab |
c | 推入 c | /ab, c |
- | 組合 -/abc | -/abc |
d | 推入 d | -/abc, d |
e | 推入 e | -/abc, d, e |
* | 組合 *de | -/abc, *de |
+ | 組合 +-/abc*de | +-/abc*de |
a | 推入 a | +-/abc*de, a |
c | 推入 c | +-/abc*de, a, c |
* | 組合 *ac | +-/abc*de, *ac |
- | 組合 -+-/abc*de*ac | -+-/abc*de*ac |
關鍵組合如下:
第 4 題24 分
Which(s) is/are true in the following descriptions? (24 pt)
(A) In polynomial addition, the number of coefficient additions is at least 1.
(B) In polynomial addition, the number of creation of new nodes is at most m+n-1.
(C) In polynomial addition, the number of creation of new nodes is at least max {m,n}.
(D) In polynomial addition, the number of coefficient additions is at most min{m,n}.
(E) In polynomial addition, the number of exponent comparisons is at most m+n.
(F) &a->val means (&a)->val
登入後即可作答並保存紀錄。
核心觀念
題目未明示 的定義及零係數項的處理方式;以下採資料結構教材的標準設定: 分別為兩個稀疏多項式的非零項數,節點依 exponent 排序,結果不儲存係數為 的項。
多項式相加本質上是兩條排序串列的合併:
- exponent 相同:進行一次 coefficient addition。
- exponent 不同:直接複製 exponent 較大的項。
- 其中一條串列結束後:直接接上另一條串列剩餘項目,不再比較 exponent。
設共有 組 exponent 相同,其中有 組相加後係數為 ,則
coefficient additions 的次數為 ;新節點數為
選項分析
-
(A) 錯誤。
兩個多項式可能沒有任何相同的 exponent。例如此時沒有 coefficient addition,次數為 ,並非至少 。
-
(B) 錯誤。
當兩個多項式沒有相同 exponent 時,每一個輸入項都會成為結果中的一個節點,因此最多可建立 個新節點,而不是 個。例如 :
結果有 個節點,但
-
(C) 錯誤。
若相同 exponent 的兩個係數相加後為 ,該項不建立節點。例如相加後為
結果只有 個節點,而
第 5 題12 分
Please complete the code of "Add to a circular queue". (12 pt)
void addq(element item)
{
/* hint: front, rear, MAX_QUEUE_SIZE, queue[] */
if (.......)
queueFull();
}
登入後即可作答並保存紀錄。
核心觀念
題目未提供 front、rear 的初始值與佇列表示法;以下採用標準的「保留一格」環狀佇列,並以 front == rear 表示空佇列。
設陣列大小為 ,索引前進一格必須使用:
此表示法中:
front指向隊首元素前一格。rear指向目前隊尾元素。front == rear表示空佇列。- 當下一個
rear位置等於front時,表示佇列已滿。
保留一格的原因是避免空佇列與滿佇列都呈現 front == rear,使兩種狀態無法區分。
解題方法
加入元素時依序進行:
- 計算
rear的下一個環狀位置。 - 若下一個位置等於
front,呼叫queueFull()。 - 佇列未滿時,將
rear後移並把元素存入該位置。
完整程式碼
第 6 題20 分
Please complete the code of "Invert single linked list". (20 pt)
typedef struct list_node *list_pointer;
typedef struct list_node {
char data;
list_pointer link;
};
list_pointer invert(list_pointer lead)
{
list_pointer middle, trail;
middle = NULL;
while (.......) {
}
return middle;
}
登入後即可作答並保存紀錄。
核心觀念
本題考查單向鏈結串列的「就地反轉」(in-place reversal)。
原本的鏈結串列如下:
反轉後必須變成:
每個節點的 data 不需修改,只需逐一改變節點的 link 指標。為避免遺失尚未處理的節點,需要使用三個指標:
lead:目前尚未反轉部分的第一個節點。middle:已完成反轉部分的第一個節點。trail:暫存lead原本指向的下一個節點。
解題方法
初始時尚未反轉任何節點,因此:
middle = NULL;
每次迴圈處理 lead 所指向的節點,操作順序如下:
-
先保存原本的下一個節點:
trail = lead->link; -
將目前節點接到已反轉串列的前端:
lead->link = middle; -
更新反轉串列的首節點:
middle = lead; -
移動到尚未處理的下一個節點:
lead = trail;
因此,迴圈條件應為:
lead != NULL
當 lead == NULL 時,代表原串列的所有節點都已處理完成,middle 即為反轉後串列的首節點。
完整程式碼
list_pointer invert(list_pointer lead)
{
list_pointer middle, trail;
middle = NULL;
while (lead != NULL) {
trail = lead->link;
lead->link = middle;
middle = lead;
lead = trail;
}
return middle;
}
正確性說明
以原串列:
第 7 題20 分
Write out the following results? (20 pt)
7(A)
void transpose (term a[], term b[])
{
int n, i, j, currentb;
n = a[0].value; // Number of terms in the input sparse matrix
if (n > 0) {
currentb = 1;
for (i = 0; i < 4; i++) // This loop seems fixed to 4 iterations
for(j = 1; j <= n; j++) // This loop iterates through terms based on n
if (a[j].col) { // Checks if the column of the j-th term is non-zero? This seems unusual.
b[currentb].row = a[j].col;
b[currentb].col = a[j].row;
b[currentb].value = a[j].value;
currentb++;
}
}
}
The input sparse matrix is represented by an array of term structs, where a[0] contains the total number of terms (value). The matrix dimensions are implicitly given by the range of row and column indices in the terms. The matrix is:
a[0] 6 6 8
1 0 0 15
2 0 3 22
3 0 5 -15
4 1 1 11
5 1 2 3
6 2 3 -6
7 4 0 91
8 5 2 28
The function transpose is intended to compute the transpose of a sparse matrix represented in this format. The transposed matrix is stored in b.
7(B)
void fast_transpose(term a[], term b[])
{
int row_terms[MAX_COL], starting_pos[MAX_COL];
int i, j, num_cols = a[0].col, num_terms = a[0].value;
b[0].row = num_cols; b[0].col = a[0].row;
b[0].value = num_terms;
if (num_terms > 0){
for (i = 0; i < num_cols; i++)
row_terms[i] = 0;
for (i = 1; i <= num_terms; i++)
row_terms[a[i].col]++;
starting_pos[0] = 1;
for (i=1; i < num_cols; i++)
starting_pos[i]=starting_pos[i-1] + row_terms[i-1];
for (i=1; i <= num_terms; i++) {
j = starting_pos[a[i].col]++;
b[j].row = a[i].col;
b[j].col = a[i].row;
b[j].value = a[i].value;
}
}
}
The input sparse matrix for this function is assumed to be the same as in 7(A). The term struct has fields row, col, and value. a[0] stores matrix dimensions and number of terms. MAX_COL is a predefined constant.
Write out the results of these transpose operations.
登入後即可作答並保存紀錄。
核心觀念
稀疏矩陣採用三元組表示法:
a[0].row:矩陣列數a[0].col:矩陣欄數a[0].value:非零項數a[i]():第 個非零項,格式為(row, col, value)
本題:
表示矩陣為 ,共有 個非零項。
轉置的規則是:
也就是交換 row 與 col,value 保持不變。
7(A) transpose
關鍵程式判讀
n = a[0].value;
因此 ,內層迴圈會處理 a[1] 到 a[8]。
if (a[j].col)
在 C 語言中,這等價於:
if (a[j].col != 0)
所以欄索引為 的項目會被跳過。
各項判斷如下:
j | a[j] (row,col,value) | 條件結果 | 每輪寫入的項目 |
|---|---|---|---|
| 1 | (0,0,15) | 否 | 跳過 |
| 2 | (0,3,22) | 是 | (3,0,22) |
| 3 | (0,5,-15) | 是 | (5,0,-15) |
| 4 | (1,1,11) | 是 | (1,1,11) |
| 5 | (1,2,3) | 是 | (2,1,3) |
| 6 | (2,3,-6) | 是 | (3,2,-6) |
| 7 | (4,0,91) | 否 | 跳過 |
| 8 | (5,2,28) | 是 | (2,5,28) |
外層迴圈:
for (i = 0; i < 4; i++)
會執行 次,但變數 i 沒有參與條件判斷或資料寫入。因此上述六個項目會被完整寫入 次。
currentb 的變化為:
實際寫入結果
以下以 b 至少能容納 b[0] 到 b[24] 為前提:
b 索引 | 寫入的 (row,col,value) |
|---|---|
b[1], b[7], b[13], b[19] | (3,0,22) |
b[2], b[8], b[14], b[20] | (5,0,-15) |
b[3], b[9], b[15], b[21] | (1,1,11) |
b[4], b[10], b[16], b[22] | (2,1,3) |
b[5], b[11], b[17], b[23] | (3,2,-6) |
b[6], b[12], b[18], b[24] | (2,5,28) |
b[0] 在此函式中完全沒有被設定,因此其內容保留為呼叫函式前的值,無法由本題資料決定。
此程式與標準轉置程式不同之處是:
for (i = 0; i < 4; i++)
以及:
if (a[j].col)
標準寫法通常須依欄索引判斷,例如 a[j].col == i,並且先設定 b[0]。依題面程式原樣執行時,會產生重複項目;若 b 只配置一般所需的 個位置,寫入 b[9] 以後會發生陣列越界,C 語言結果屬未定義行為。
複雜度
令非零項數為 :
- 時間複雜度:,故為
- 區域變數額外空間:
- 輸出陣列需容納本程式寫出的項目,大小為
7(B) fast_transpose
初始化標頭
int num_cols = a[0].col;
int num_terms = a[0].value;
因此:
b[0].row = 6;
b[0].col = 6;
b[0].value = 8;
所以: