109 高普考高考三級 資訊處理 資料結構 考古題詳解
第 一 題
考慮數字 到 。若將其順序重新排列,每個排列順序都稱作一個排列或置換(Permutation)。例如, 是 的一個排列。
我們可以將數字 到 的排列視為一個順序映射 ,則前述例子可表示為: 、、、、。
當然, 也是 的一個排列。
在一個數字 到 的排列 中,若一對數字 和 滿足 且 ,也就是在排列 中較大的數字 出現在較小的數字 左邊(前面),我們稱此對數字為反向(Inversion)。排列 的反向數(Inversion Number)則定義為排列 中反向的總數量。
請回答下列問題:
(一)數字 到 的何種排列會有最大的反向數?最大反向數是多少?(5分)
(二)若給定一個數字 到 的排列 ,請提出一個線性遞迴(Linear Recursive)的方式來算出排列 的反向數,並提供虛擬碼(Pseudo-code)與時間複雜度分析。(10分)
登入後即可作答並保存紀錄。
核心觀念
- 反向(Inversion)的定義與數學性質: 在 到 的排列中,對於任一對元素 ,若 且較大的數字 出現在較小的數字 前面,即稱此對為一個「反向」。由 個相異元素構成的排列中,兩兩配對的總對數為組合數 。
- 排列映射 的意義: 題目的 定義為「數值到出現位置」的映射。若 出現在排列的第 個位置,則 。因此,「較大數字 出現在較小數字 左邊」在數學上等價於 。
- 線性遞迴(Linear Recursion): 線性遞迴是指函式在每次執行時,至多只呼叫一次自身(單一遞迴分支),使遞迴呼叫鏈呈一條線性鏈狀結構(問題規模由 降至 )。這與分治法(Divide-and-Conquer,如合併排序法計算反向數採用的二元分支遞迴 )在結構上截然不同。
解題方法
(一)最大反向數與對應排列推導
- 排列總對數: 集合 中,滿足 的數對 共有:
- 最大反向條件: 當且僅當排列中的「每一對」數字 ()皆滿足 (即較大的數恆出現在較小的數之前)時,反向數達到最大值。
- 對應排列型式: 所有較大數字必須排在所有較小數字的前方,因此該排列必為嚴格由大到小的降冪排列(完全逆序排列): 以題目所定義的位置映射 表示為: 其最大反向數即為所有數對的總數:。
(二)線性遞迴演算法設計與複雜度分析
1. 遞迴切入點與數學推導
設排列中所有反向數對的集合為:
我們將全體反向對依據「是否包含最大數字 」分割為兩互斥子集:
- 包含最大數字 的反向對:即所有形如 且滿足 與 的數對。我們只需走訪 到 ,統計滿足 的個數。
- 不包含數字 的反向對:即由數字集合 彼此之間構成的反向對。因為移除數字 並不會改變剩餘任意兩數 在排列中的相對先後順序( 的真假值完全不變),此子集的大小恰好為規模為 的子問題反向數。
由此可建立線性遞迴關係式: 其中 為指示函數,條件成立時計為 ,否則計為 。
2. 虛擬碼(Pseudo-code)
假設 為一維陣列(索引 到 ), 存放數值 在排列中的位置(1-based):
第 二 題
優先佇列(Priority Queue)是依管理物件的優先權來考量。在此考慮管理物件的鍵值(Key)愈小,其優先權愈高;兩個主要操作分別為加入(Insert)與擷取最小者(Delete_Min)。
(一)請說明如何利用優先佇列對 個鍵值進行排序。(6分)
(二)使用一個未排序的陣列(Unsorted Array)來管理鍵值,以實現一個優先佇列,請回答下列問題。(10分)
-
若有 個鍵值,請說明兩個主要操作——加入(Insert)與擷取最小者(Delete_Min)——的時間複雜度。
-
請判斷下列敘述是否為真,並說明原因:
若以此優先佇列進行排序(Sorting),其所對應的排序原理為插入排序(Insertion Sort)。
(三)二元堆積(Binary Heap)是一個優先佇列的資料結構。因為鍵值小的物件有較高的優先權,所以又可稱為最小堆積(Minimum Heap)。(14分)
- 在結構上,最小堆積為一個完全二元樹(Complete Binary Tree)。若使用一個陣列來實作最小堆積,陣列中物件的鍵值放置如下,請描述此陣列對應的完全二元樹(以樹狀結構表示)。
| Index | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| Key | 35 | 18 | 42 | 24 | 7 | 14 | 25 | 12 | 38 | 21 |
-
請說明二元堆積中何謂堆積特性(Heap Property)。
-
前述第 1 小題中的完全二元樹並未具備堆積特性,請將其進行堆積化(Heapify),並以陣列表示出堆積化後的最小堆積所對應之完全二元樹。
登入後即可作答並保存紀錄。
核心觀念
本題評量重點涵蓋「優先佇列(Priority Queue, PQ)抽象資料型態」與「內部實作結構(陣列 vs. 二元堆積)」之間的對應關係,具體核心包含:
- 優先佇列排序架構(PQ Sort):利用連續「加入(Insert)」與「擷取(Delete_Min)」完成排序。不同的內部實作(未排序陣列、已排序陣列、堆積)會對應到不同的經典排序演算法(選擇排序、插入排序、堆積排序)。
- 未排序陣列實作之複雜度權衡(Trade-off):尾端插入為常數時間 ,但尋找並刪除最小值需循序走訪,花費線性時間 。
- 完全二元樹(Complete Binary Tree, CBT)之循序表示法:利用 1-based 陣列索引對應樹狀結構,節點 的父節點為 ,左子節點為 ,右子節點為 。
- 最小堆積特性(Min-Heap Property)與堆積化(Heapify):任意非根節點的鍵值必大於或等於其父節點鍵值。由最後一個非葉節點 起算,由下而上(Bottom-up)對各子樹執行下沉(Sift-Down / Percolate-Down),以 時間建構最小堆積。
解題方法
(一)利用優先佇列對 個鍵值進行排序
利用優先佇列進行排序的程序統稱為 PQ-Sort,分為兩大階段:
- 建立階段(Construction Phase):
- 建立一個空的優先佇列 。
- 將待排序的 個鍵值依序透過
Insert操作加入 中。
- 擷取輸出階段(Extraction Phase):
- 連續執行 次
Delete_Min操作。 - 由於每次
Delete_Min都會取出當前 中鍵值最小的元素,因此將取出的元素依序填入輸出陣列的第 到第 個位置,即完成由小到大的遞增排序。
(二)使用未排序陣列(Unsorted Array)實現優先佇列
1. 操作之時間複雜度分析
以長度為 的未排序陣列實作:
Insert(加入): 直接將新元素置於陣列末端(索引 處),並將元素計數器加 1。- 時間複雜度:。
Delete_Min(擷取最小者): 由於陣列無序,必須從頭到尾掃描所有元素以找出最小值所在的索引位置,需比較 次;找到後,將最小值取出,並將陣列最後一個元素移至該空缺處(或將後續元素往前遞移)以維持緊湊性。- 時間複雜度:。
2. 排序原理判斷
該敘述為非(錯誤),其對應之排序原理應為選擇排序(Selection Sort)。
(三)二元堆積(最小堆積)之結構與操作
1. 陣列對應之完全二元樹
陣列大小 ,利用 1-based 索引規則:
- 根節點:Index 1(鍵值 35)
- Index 1 的子節點:左子為 Index 2(鍵值 18)、右子為 Index 3(鍵值 42)
- Index 2 的子節點:左子為 Index 4(鍵值 24)、右子為 Index 5(鍵值 7)
- Index 3 的子節點:左子為 Index 6(鍵值 14)、右子為 Index 7(鍵值 25)
- Index 4 的子節點:左子為 Index 8(鍵值 12)、右子為 Index 9(鍵值 38)
- Index 5 的子節點:左子為 Index 10(鍵值 21)
樹狀結構表示如下:
35 (1)
/ \
18 (2) 42 (3)
/ \ / \
24 (4) 7(5) 14(6) 25(7)
/ \ /
12(8) 38(9) 21(10)
2. 堆積特性(Heap Property)
在二元堆積中,堆積特性指的是樹中節點鍵值與其子節點鍵值之間的偏序關係:
- 最小堆積特性(Min-Heap Property):對於樹中除根節點以外的任意節點 ,其鍵值恆大於或等於其父節點(Parent)的鍵值,即: 換言之,任一子樹的根節點鍵值,皆為該子樹所有節點中的最小值。
3. 堆積化(Heapify)過程與結果
節點總數 ,最後一個非葉節點索引為 。自底向上依序對 Index 進行下沉(Sift-Down)調整:
- 調整 Index 5(鍵值 7):
- 子節點為 Index 10(鍵值 21)。
- 比較 ,符合最小堆積特性,不需調整。
- 調整 Index 4(鍵值 24):
- 子節點為 Index 8(鍵值 12)與 Index 9(鍵值 38)。
- 較小的子節點為 Index 8(鍵值 12)。
- 因 ,將 Index 4 與 Index 8 交換。
- 交換後陣列:
[-, 35, 18, 42, 12, 7, 14, 25, 24, 38, 21]。
- 調整 Index 3(鍵值 42):
- 子節點為 Index 6(鍵值 14)與 Index 7(鍵值 25)。
- 較小的子節點為 Index 6(鍵值 14)。
- 因 ,將 Index 3 與 Index 6 交換。
- 交換後陣列:
[-, 35, 18, 14, 12, 7, 42, 25, 24, 38, 21]。
- 調整 Index 2(鍵值 18):
- 子節點為 Index 4(鍵值 12)與 Index 5(鍵值 7)。
第 三 題
請回答下列關於 AVL 樹(AVL Tree)的問題:
(一)我們欲將所管理的鍵值(Key)依序列出,請問是否可以利用一個 AVL 樹對鍵值進行排序(Sorting)?若不行,請說明原因;如果可以,請描述方法及時間複雜度。(5分)
(二)請提供一個線性時間的演算法,判斷一個二元搜尋樹是否為 AVL 樹。(10分)
(三)在 AVL 樹上進行一次加入(Insert)操作後,是否最多只需要一次的重構(Restructuring)即可恢復其平衡特性?請說明原因。(10分)
登入後即可作答並保存紀錄。
核心觀念
- AVL 樹之定義與平衡特性:AVL 樹為一種高度平衡的二元搜尋樹(Height-Balanced Binary Search Tree)。對樹中任一節點 ,其左子樹高度 與右子樹高度 滿足平衡因子(Balance Factor, )條件:
- 二元搜尋樹與中序走訪的排序等價性:二元搜尋樹(BST)的中序走訪(In-order Traversal)依「左子樹 根節點 右子樹」順序輸出,其鍵值必然呈現嚴格遞增排序。
- 由下而上(Bottom-Up)走訪與剪枝:檢驗樹結構特性時,自頂向下(Top-Down)重複計算樹高會導致 的最差時間複雜度;改採後序走訪(Post-order Traversal)由葉節點向根節點計算高度並同步驗證平衡因子,可在每個節點僅造訪常數次的前提下,於 線性時間內完成驗證。
- 插入操作的高度不變性(Height Invariant):AVL 樹插入新節點引發失衡時,透過單旋轉(Single Rotation: LL, RR)或雙旋轉(Double Rotation: LR, RL,亦即 Trinode Restructuring)修正最深失衡節點後,該子樹的高度會精準回復至「插入前」的原高度,因此不平衡狀態不會向更高層的祖先節點傳遞。
解題方法
(一)利用 AVL 樹進行排序之可行性、方法與時間複雜度
可以利用 AVL 樹對鍵值進行排序。此演算法即為樹排序(Tree Sort)的平衡樹優化版本。
- 排序方法:
- 建樹階段:初始化一棵空的 AVL 樹。將欲排序的 個鍵值依序呼叫插入操作(Insert)加入 AVL 樹中。由於 AVL 樹具備自我平衡機制,每次插入均維持平衡。
- 輸出階段:當 個鍵值全數插入後,對此 AVL 樹執行中序走訪(In-order Traversal),將走訪到的鍵值依序輸出,即得到由小到大排列之有序數列。
- 時間複雜度推導:
- 建樹時間:一棵包含 個節點的 AVL 樹,其樹高嚴格受限於 。因此,插入第 個元素的成本為 。插入 個元素的總建樹時間為:
- 走訪時間:中序走訪每個節點恰好造訪一次,時間複雜度為 。
- 總時間複雜度:建樹時間加上走訪時間,總時間複雜度為:
- (空間複雜度為儲存樹節點所需之 空間)。
(二)線性時間判斷二元搜尋樹是否為 AVL 樹之演算法
題目已知該樹已具備二元搜尋樹(BST)之特性(鍵值滿足 ),故判斷重點在於驗證整棵樹是否滿足 AVL 樹的「平衡因子限制(任一節點 )」。
- 演算法切入點: 若由根節點自頂向下對每個節點呼叫求高度函式,會造成大量重複計算,使時間複雜度退化至 。必須採用後序走訪(Post-order Traversal)的由下而上(Bottom-Up)遞迴:
- 遞迴計算左子樹與右子樹的高度。
- 若任何子樹失衡,立即回傳特定哨兵值(如 )提早剪枝終止。
- 若左右子樹皆平衡,檢查當前節點平衡因子 是否大於 ;若大於 則回傳 ,否則回傳當前子樹的真實高度 。
- 演算法虛擬碼(C/C++ 風格):
第 四 題
若使用相鄰矩陣(Adjacency Matrix) 來表示圖一中的無向圖 ,請考慮下列問題。
【圖一:無向圖 】
(一)對於無向圖 :(12分)
-
請給出對應的相鄰矩陣 。
-
以字母順序為考量進行深度優先搜尋(Depth-First Search, DFS),請由節點 開始,描述此深度優先搜尋所產生的深度優先樹(DF-tree)。
(二)請說明在使用相鄰矩陣(Adjacency Matrix)表示的無向圖上,進行深度優先搜尋的時間複雜度,其中節點與邊的數量分別為 與 。(8分)
(三)若將圖一無向圖 中的邊給予方向,成為如圖二中的有向圖(Directed Graph):(10分)
【圖二:有向圖 】
-
有向圖 沒有迴圈(Cycle),是一個無迴圈有向圖(Directed Acyclic Graph, DAG),所以存在節點的拓樸排序(Topological Sort)。請對 給出一個拓樸排序(Topological Sort)。
-
請給出一個方法,判斷一個有向圖是否沒有迴圈。
登入後即可作答並保存紀錄。
核心觀念
- 無向圖的相鄰矩陣中,若頂點 與 相鄰,則 ;否則為 。無向圖的矩陣必須對稱,沒有自迴圈時主對角線為 。
- DFS 依指定的鄰點順序,先沿著尚未拜訪的鄰點深入;無向圖中,首次發現頂點時所走的邊構成深度優先樹(DF-tree)。
- 有向無迴圈圖(DAG)的拓樸排序,必須讓每一條有向邊 的起點 排在終點 前面。
(一)無向圖
1. 相鄰矩陣
依字母順序排列頂點為 。由圖一可讀出邊為:
因此相鄰矩陣為:
矩陣對稱,且主對角線皆為 ,符合此圖是無自迴圈無向圖的性質。
2. 由 開始的 DFS 深度優先樹
每次都按字母順序檢查鄰點。由矩陣可得各頂點的鄰點順序,例如 的鄰點依序是 , 的鄰點依序是 。
搜尋過程的拜訪順序為:
沒有尚未拜訪的鄰點後,回到 ,再拜訪 ;回到 後,再依序拜訪 ;回到 後,再拜訪 ,並由 拜訪 。完整拜訪順序為:
DF-tree 的樹邊為:
(二)相鄰矩陣上的 DFS 時間複雜度
使用相鄰矩陣時,DFS 每拜訪一個頂點,都要掃描該頂點所在的一整列,檢查是否有通往其他頂點的邊。每列有 個位置,共有 個頂點,因此: