108 年 國立臺灣大學工程科學及海洋工程學系碩士班丁組《資料結構(A)》

📄 試題原卷 免費註冊後即可對照原始考卷 PDF免費註冊

第 1 題10 分

(10%) Please convert the following infix expressions to postfix:
(a) (5%) a∗(b−c)/da * (b - c) / d
(b) (5%) a+(b∗c−d)∗(e+f/g)a + (b * c - d) * (e + f / g)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

中序式(infix)將運算子放在兩個運算元之間,例如 a+ba+b;後序式(postfix)則將運算子放在其運算元之後,例如 ab+ab+。

轉換時遵守:

  • 括號優先處理,括號本身不輸出。
  • 乘法與除法優先於加法與減法。
  • 同一優先權由左至右計算。
  • 後序式中,每個運算子都接在其完整運算子之前。

運算子優先權如下:

∗、/  >  +、−*、/ \;>\; +、-

解題方法

採用「運算子堆疊法」:

  1. 運算元直接輸出。
  2. 左括號 (( 推入堆疊。
  3. 遇到右括號 )),持續彈出並輸出堆疊頂端運算子,直到遇到左括號;再將左括號移除。
  4. 遇到運算子時,若堆疊頂端已有相同或更高優先權的運算子,先彈出輸出,再將目前運算子推入。
  5. 掃描結束後,將堆疊中剩餘運算子依序彈出。

(a)a∗(b−c)/da * (b - c) / d

原式的運算結構為:

a×(b−c)d\frac{a \times (b-c)}{d}

轉換步驟:

  1. 讀到 aa,直接輸出:aa
  2. 讀到 ∗*,推入堆疊。
  3. 讀到左括號,推入堆疊。
  4. 讀到 bb,輸出:abab
  5. 讀到 −-,推入堆疊。
  6. 讀到 cc,輸出:abcabc
  7. 讀到右括號,彈出 −-,輸出:abc−abc-;移除左括號。
  8. 讀到 //。堆疊頂端為 ∗*,且 ∗* 與 // 優先權相同,依左至右規則先輸出 ∗*:abc−∗abc-*
  9. 將 // 推入堆疊。
  10. 讀到 dd,輸出:abc−∗dabc-*d
  11. 掃描結束,彈出 //。

因此:

a (b c −) ∗ d /\boxed{a\ (b\ c\ -)\ *\ d\ /}

不加空格時為:

abc−∗d/\boxed{abc-*d/}

(b)a+(b∗c−d)∗(e+f/g)a + (b * c - d) * (e + f / g)

原式的運算結構為:

a+((b×c)−d)×(e+fg)a + \bigl((b \times c)-d\bigr)\times\left(e+\frac{f}{g}\right)

轉換步驟:

  1. 讀到 aa,輸出:aa
  2. 讀到 ++,推入堆疊。
  3. 讀到左括號,推入堆疊。
  4. 讀到 bb,輸出:abab
  5. 讀到 ∗*,推入堆疊。
  6. 讀到 cc,輸出:abcabc
  7. 讀到 −-。堆疊頂端為 ∗*,優先權較高,先輸出 ∗*:abc∗abc*
  8. 將 −- 推入堆疊。
  9. 讀到 dd,輸出:abc∗dabc*d
🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 2 題15 分

Write a method called makeList in a SList class with a tail. The makeList function takes an array counts of int, constructs a singly-linked list, and associates its head and tail pointers to the corresponding SListNode nodes. In the constructed list, the first counts[0] items are the number counts[0], the next counts [1] items are the number counts[1], etc. For example, if the input array is {1 3 2}, then the output linked list is: head→ 1 →3→3→3→2→2→tail. Define the SList and SListNode classes to complete the make List method. When it is done, the size of nodes in SList should also be updated accordingly.

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查:

  1. 單向鏈結串列(Singly Linked List)
  2. 頭指標 head 與尾指標 tail 的維護
  3. 尾端插入(append)
  4. 鏈結串列節點數量 size 的更新

給定陣列 counts 後,對每個索引 ii:

  • 建立 counts[i] 個節點;
  • 每個節點的資料值都設為 counts[i]。

因此,若:

counts = {1, 3, 2}

則建立:

1 → 3 → 3 → 3 → 2 → 2

最後:

  • head 指向第一個節點;
  • tail 指向最後一個節點;
  • size = 1 + 3 + 2 = 6。

尾指標 tail 指向最後一個節點,而不是額外建立的虛擬節點;最後一個節點的 next 應為 null。


解題方法

建立節點時,分成兩種情況處理。

情況一:串列目前為空

若 head == null,代表這是建立的第一個節點,因此:

head = newNode
tail = newNode

此時頭尾指標都指向同一個節點。

情況二:串列已有節點

若串列已經有節點,將新節點接在尾端:

tail.next = newNode
tail = newNode

每建立一個節點,就將:

size++

如此即可同步維護節點數量。


完整類別與方法

以下以 Java 語法實作:

class SListNode {
    int data;
    SListNode next;

    SListNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class SList {
    SListNode head;
    SListNode tail;
    int size;

    public SList() {
        head = null;
        tail = null;
        size = 0;
    }

    public void makeList(int[] counts) {
        // 重新建立串列前,先初始化串列狀態
        head = null;
        tail = null;
        size = 0;

        for (int i = 0; i < counts.length; i++) {
            int value = counts[i];

            if (value < 0) {
                throw new IllegalArgumentException(
                    "counts 中的元素不可為負數"
                );
            }

            // 建立 value 個資料值為 value 的節點
            for (int j = 0; j < value; j++) {
                SListNode newNode = new SListNode(value);

                if (head == null) {
                    // 建立第一個節點
                    head = newNode;
                    tail = newNode;
                } else {
                    // 接到尾端
                    tail.next = newNode;
                    tail = newNode;
                }

                size++;
            }
        }
    }
}

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 3 題10 分

Examine the following statements. If it is correct, prove it. If it is incorrect, show the contradiction.
(a) (5%) 1/(n−50)∈O(n)1 / (n - 50) \in O(n)
(b) (5%) n2/(n+log⁡n)∈Θ(n2)n^2 / (n + \log n) \in \Theta(n^2)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查漸進符號的定義:

  • f(n)∈O(g(n))f(n)\in O(g(n)):存在常數 c>0c>0 與 n0n_0,使得對所有 n≥n0n\ge n_0,
    0≤f(n)≤c g(n)。0\le f(n)\le c\,g(n)。

  • f(n)∈Θ(g(n))f(n)\in \Theta(g(n)):存在常數 c1,c2>0c_1,c_2>0 與 n0n_0,使得對所有 n≥n0n\ge n_0,
    0≤c1g(n)≤f(n)≤c2g(n)。0\le c_1g(n)\le f(n)\le c_2g(n)。

判斷時只需考慮 nn 足夠大時的成長率,有限個小 nn 值不影響 OO 或 Θ\Theta 的判定。

解題方法與選項分析

(a)1n−50∈O(n)\displaystyle \frac{1}{n-50}\in O(n)

此敘述正確。

當 n≥100n\ge 100 時,

n−50≥n2。n-50\ge \frac{n}{2}。

因此,

1n−50≤2n≤2n(n≥100)。\frac{1}{n-50} \le \frac{2}{n} \le 2n \qquad (n\ge 100)。

取 c=2c=2、n0=100n_0=100,即可符合 O(n)O(n) 的定義:

0≤1n−50≤2n。0\le \frac{1}{n-50}\le 2n。

所以,

1n−50∈O(n)。\boxed{\frac{1}{n-50}\in O(n)}。

事實上,當 nn 足夠大時,

1n−50=Θ(1n),\frac{1}{n-50}=\Theta\left(\frac{1}{n}\right),

而 1n=O(n)\frac{1}{n}=O(n),故原敘述成立。

(b)n2n+log⁡n∈Θ(n2)\displaystyle \frac{n^2}{n+\log n}\in\Theta(n^2)

此敘述錯誤。

對足夠大的 nn,有 log⁡n≤n\log n\le n,因此

n≤n+log⁡n≤2n。n\le n+\log n\le 2n。

取倒數後得

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 4 題10 分

Show the sequence of swaps by which the method bottomUpHeap converts the following array into a min heap. Redraw the array once for each swap.
7 2 6 9 3 5 1 8

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查以 bottom-up heap construction 建立最小堆積(min heap)。

採用 1-based indexing 時:

  • 節點 ii 的左子節點為 2i2i
  • 右子節點為 2i+12i+1
  • 節點 ii 的父節點為 ⌊i/2⌋\lfloor i/2\rfloor
  • 最後一個非葉節點為 ⌊n/2⌋\lfloor n/2\rfloor

建立最小堆積時,從最後一個非葉節點開始,依序對

i=⌊n2⌋,⌊n2⌋−1,…,1i=\left\lfloor\frac n2\right\rfloor,\left\lfloor\frac n2\right\rfloor-1,\ldots,1

執行向下調整(sift-down)。每次將目前節點與其值最小的子節點比較;若目前節點較大,就交換,並繼續向下調整。


解題方法

原始陣列共有 n=8n=8 個元素,因此最後一個非葉節點為

⌊82⌋=4\left\lfloor\frac 82\right\rfloor=4

處理順序為 4,3,2,14,3,2,1。

原始陣列:

[7, 2, 6, 9, 3, 5, 1, 8][7,\ 2,\ 6,\ 9,\ 3,\ 5,\ 1,\ 8]

1. 處理索引 4

索引 4 的值為 99,其子節點為索引 8,值為 88。

因為 9>89>8,交換索引 4 與索引 8:

[7, 2, 6, 8, 3, 5, 1, 9][7,\ 2,\ 6,\ 8,\ 3,\ 5,\ 1,\ 9]

索引 8 已是葉節點,調整結束。


2. 處理索引 3

索引 3 的值為 66,其子節點為:

  • 索引 6:55
  • 索引 7:11

兩個子節點中較小者為 11。因為 6>16>1,交換索引 3 與索引 7:

[7, 2, 1, 8, 3, 5, 6, 9][7,\ 2,\ 1,\ 8,\ 3,\ 5,\ 6,\ 9]

索引 7 為葉節點,調整結束。


3. 處理索引 2

索引 2 的值為 22,其子節點為:

  • 索引 4:88
  • 索引 5:33

最小子節點為 33。因為 2<32<3,已符合最小堆積條件,不需交換:

[7, 2, 1, 8, 3, 5, 6, 9][7,\ 2,\ 1,\ 8,\ 3,\ 5,\ 6,\ 9]

4. 處理索引 1

索引 1 的值為 77,其子節點為:

  • 索引 2:22
  • 索引 3:11

最小子節點為 11。因為 7>17>1,交換索引 1 與索引 3:

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 5 題15 分

Write a recursive method preorderElementAt(int k) that returns the kth element in the preorder listing of the subtree in a binary tree. For example, if k = 1, return the element in the root node. The method should run in O(d) time, where d is the depth of the subtree. If the subtree doesn't have k nodes, return null. Define all necessary classes and associated fields to accomplish the function.

登入後即可作答並保存紀錄。

這一題的完整詳解

解題關鍵

  • 前序遍歷順序:根 → 左子樹 → 右子樹。
  • 若每個節點保存其整個子樹的節點數 subtreeSize,則可在 O(d)O(d)(dd 為子樹深度)內定位第 kk 個節點:
    若 k=1⇒回傳根元素\text{若 }k=1\Rightarrow\text{回傳根元素}
    若 k−1≤左子樹大小⇒在左子樹遞迴 preorderElementAt(k−1)\text{若 }k-1\le\text{左子樹大小}\Rightarrow\text{在左子樹遞迴 }preorderElementAt(k-1)
    否則⇒在右子樹遞迴 preorderElementAt(k−1−左子樹大小)\text{否則}\Rightarrow\text{在右子樹遞迴 }preorderElementAt\big(k-1-\text{左子樹大小}\big)

類別與欄位

public class TreeNode<E> {
    E element;                 // 節點儲存的資料
    TreeNode<E> left, right;   // 左、右子節點
    int subtreeSize;           // 以此節點為根的子樹節點總數(含自身)

    public TreeNode(E e) {
        element = e;
        left = right = null;
        subtreeSize = 1;       // 初始只有自己
    }

    // 更新 subtreeSize,通常在插入/刪除時呼叫
    void updateSize() {
        int l = (left  != null) ? left.subtreeSize  : 0;
        int r = (right != null) ? right.subtreeSize : 0;
        subtreeSize = 1 + l + r;
    }
}

遞迴方法

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 6 題12 分

A graph G is bipartite if its vertices can be partitioned into two sets X and Y such that every edge in G has one end vertex in X and the other in Y. Describe (not code) an efficient algorithm for determining if an undirected graph G is bipartite (without knowing the sets X and Y in advance).

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

二分圖(bipartite graph)的定義是:可以將所有頂點分成兩個互斥集合 XX 與 YY,使每條邊的兩端分別位於 XX 與 YY。

等價地,二分圖可以使用兩種顏色為頂點著色,使得每條邊連接不同顏色的頂點。因為若將兩種顏色的頂點分別放入 XX 與 YY,即可得到二分圖的兩個集合。

判定的關鍵原則為:

  • 與某頂點距離為偶數的頂點使用同色。
  • 與某頂點距離為奇數的頂點使用異色。
  • 若發現某條邊的兩端已被分配相同顏色,則圖形不是二分圖。

此方法也等價於判斷圖中是否含有奇數長度的環:

G 為二分圖  ⟺  G 不含奇數長度的環G\text{ 為二分圖}\iff G\text{ 不含奇數長度的環}

解題方法

採用 BFS 或 DFS 進行圖形走訪,並在走訪過程中進行二色著色。

令每個頂點的顏色為:

  • 未著色
  • 顏色 00
  • 顏色 11

演算法步驟如下:

  1. 將所有頂點設為未著色。
  2. 逐一檢查每個頂點。若某頂點尚未著色,將它指定為顏色 00,並從該頂點開始 BFS 或 DFS。
  3. 走訪目前頂點 uu 的每一條邊 (u,v)(u,v):
    • 若 vv 尚未著色,將 vv 設為與 uu 不同的顏色,即 color[v]=1−color[u]color[v]=1-color[u] 然後繼續走訪 vv。
    • 若 vv 已著色,檢查是否滿足 color[v]≠color[u]color[v]\neq color[u]
    • 若 color[v]=color[u]color[v]=color[u],表示存在衝突,立即判定 GG 不是二分圖。
  4. 若所有連通元件都完成走訪且沒有發生衝突,則判定 GG 是二分圖。此時可將顏色 00 的頂點放入 XX,顏色 11 的頂點放入 YY。

必須逐一處理所有尚未著色的頂點,因為無向圖可能不是連通圖。每個連通元件都必須分別進行二色著色。

正確性說明

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 7 題16 分

Sorting.
(a) (8%) Trace the bubble sort as it sorts the following array into ascending order:
15 4 2 27 10
(b) (8%) Is the bucket-sort algorithm in-place? Why or why not?

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查兩種排序法:

  • Bubble sort:反覆比較相鄰元素,若左值大於右值便交換,使較大的元素逐步往陣列右側移動。
  • Bucket sort:先將資料依值域分配至多個桶子,再分別排序各桶並串接;判斷是否為 in-place 的重點,在於是否需要與輸入陣列大小無關的額外儲存空間。

(a) Bubble sort 排序過程

初始陣列為:

1542271015\quad 4\quad 2\quad 27\quad 10

採用由左至右掃描的標準 Bubble sort,每一輪結束後,當輪最大的未排序元素會固定在最右側。

第 1 輪

依序比較相鄰元素:

  1. 比較 1515 與 44:
15>4⇒交換15>4\Rightarrow 交換 415227104\quad 15\quad 2\quad 27\quad 10
  1. 比較 1515 與 22:
15>2⇒交換15>2\Rightarrow 交換 421527104\quad 2\quad 15\quad 27\quad 10
  1. 比較 1515 與 2727:
15<27⇒不交換15<27\Rightarrow 不交換 421527104\quad 2\quad 15\quad 27\quad 10
  1. 比較 2727 與 1010:
27>10⇒交換27>10\Rightarrow 交換 421510274\quad 2\quad 15\quad 10\quad 27

第 1 輪結束:

42151027\boxed{4\quad 2\quad 15\quad 10\quad 27}

此時最大值 2727 已固定在最後位置。

第 2 輪

只需處理前四個元素:

  1. 比較 44 與 22:
4>2⇒交換4>2\Rightarrow 交換 241510272\quad 4\quad 15\quad 10\quad 27
  1. 比較 44 與 1515:
4<15⇒不交換4<15\Rightarrow 不交換
  1. 比較 1515 與 1010:
15>10⇒交換15>10\Rightarrow 交換 241015272\quad 4\quad 10\quad 15\quad 27

第 2 輪結束:

24101527\boxed{2\quad 4\quad 10\quad 15\quad 27}

此時 1515 已固定在倒數第二個位置。

第 3 輪

處理前面三個元素:

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

第 8 題12 分

Suppose you insert a set of keys {4, 6, 12, 15, 3, 5} into an initially empty 2-3-4 tree in that order. Now, you do the same insertion to an empty 2-3-4 tree with the same keys but a different order, say {12, 3, 6, 4, 5, 15}. Can these two trees have the same structure? Draw both trees to justify your answer.

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

2-3-4 樹是階數為 44 的 B-tree,具有以下性質:

  • 每個節點最多含 33 個鍵值。

  • 節點內鍵值皆由小到大排列。

  • 除根節點外,每個內部節點至少含 11 個鍵值。

  • 所有葉節點位於同一層。

  • 插入時,若即將進入的節點已含 33 個鍵值,必須先分裂:

    [a,b,c]⟶[a]↑b[c][a,b,c]\longrightarrow [a]\quad \uparrow b\quad [c]

也就是將中間鍵值 bb 提升至父節點。

本題考查「插入順序是否必然造成不同的 2-3-4 樹結構」。答案取決於每次分裂時被提升的鍵值與最後各子樹的分布。


解題方法:第一種插入順序

插入順序為:

4,6,12,15,3,54,6,12,15,3,5

  1. 插入 4,6,124,6,12:

[4,6,12][4,6,12]

  1. 插入 1515 前,根節點已滿,先分裂:

[4,6,12]⟶[4]↑6[12][4,6,12]\longrightarrow [4]\quad \uparrow 6\quad [12]

再將 1515 插入右子節點:

[6]/\[4][12,15]\begin{array}{c} [6]\\ /\quad \backslash\\ [4]\quad [12,15] \end{array}
  1. 插入 33:

[4]⟶[3,4][4]\longrightarrow [3,4]

  1. 插入 55:

[3,4]⟶[3,4,5][3,4]\longrightarrow [3,4,5]

因此第一棵樹為:

          [6]
         /   \
 [3, 4, 5]  [12, 15]

解題方法:第二種插入順序

插入順序為:

12,3,6,4,5,1512,3,6,4,5,15

  1. 插入 12,3,612,3,6:

[3,6,12][3,6,12]

🔒

後續完整解題步驟與【答案】

免費註冊,享三天全站完整詳解閱覽。

免費註冊

其他考古題