108 年 國立暨南國際大學資訊工程學系碩士班《資料結構與演算法》
第 1 題
a. How many different binary trees can be made from 5 nodes? Explain your answer. (20%)
b. Suppose a binary tree is used to store N nodes, show the possibly minimum tree height. Explain your answer. (10%)
登入後即可作答並保存紀錄。
核心觀念
本題考查兩個重要觀念:
- 具有 個節點之不同二元樹數量:使用 Catalan number(卡特蘭數)。
- 二元樹的最小高度:利用高度為 時最多能容納的節點數,求出能容納 個節點的最小高度。
a. 由 5 個節點可形成多少種不同二元樹?
解題方法
令 表示由 個節點形成的不同二元樹數量。
一棵二元樹若選定根節點,剩餘的 個節點會分配到:
- 左子樹: 個節點;
- 右子樹: 個節點。
因此:
其中空樹也要計算,因此定義:
逐步計算:
也可直接使用 Catalan number 公式:
對於 :
解題技巧
二元樹節點數量的排列為:
這就是 Catalan number 序列。考試中若題目問「由 個節點可形成多少種不同二元樹」,通常直接套用:
此處的「不同」採標準定義,指樹的形狀不同。若題目另指 5 個節點具有不同標籤,則還需再乘上 ;本題依二元樹結構計數,答案為 。
第 2 題
- For the following key sequence, "ADIAMONDISFOREVER",
a. create a 2-3-4 tree. Explain your answer in detail. (15%)
b. draw a red-black tree to represent your 2-3-4 tree in the previous question. (15%)
c. explain the structural properties of the red-black trees. (15%)
登入後即可作答並保存紀錄。
(a) 建立 2-3-4 樹與詳細解說
核心觀念與規則:
2-3-4 樹為階數 (order) 為 4 的 B-tree。本解法採用 Top-Down(由上而下)預先分裂策略:搜尋插入位置時,若遇到含有 3 個 key 的滿節點 (4-node),先將中間鍵值向上提升 (promote) 至父節點,將該 4-node 分裂為兩個 2-node,再繼續往下插入。若 key 值重複,依據 置於左子樹處理。
關鍵建構步驟(字元序列:ADIAMONDISFOREVER):
- 插入 A, D, I:節點滿載為 4-node
[A, D, I]。 - 插入 A:根節點
[A, D, I]滿載,預先分裂中間值 D 成為新根。- 分裂後:根
[D],左子[A],右子[I]。 - 插入 A 後左子成為
[A, A]。
- 分裂後:根
- 插入 M, O:M 與 O 依序進入右子,形成 4-node
[I, M, O]。 - 插入 N:搜尋至
[I, M, O]發現滿載,將 M 提升至根節點。- 分裂後:根
[D, M],子節點由左至右為[A, A]、[I]、[O]。 - 插入 N 至
[O]形成[N, O]。
- 分裂後:根
- 插入 D, I, S, F:
- 插入 D 至
[A, A]形成 4-node[A, A, D]。 - 插入 I 至
[I]形成[I, I]。 - 插入 S 至
[N, O]形成 4-node[N, O, S]。 - 插入 F 至
[I, I]形成 4-node[F, I, I]。
- 插入 D 至
- 插入 O:搜尋至
[N, O, S]發現滿載,將 O 提升至根節點。- 分裂後:根
[D, M, O]滿載,子節點由左至右為[A, A, D]、[F, I, I]、[N]、[S]。 - 插入 O 至
[N]形成[N, O]。
- 分裂後:根
- 插入 R:根節點
[D, M, O]滿載,預先分裂中間值 M 成為新根。- 分裂後:根
[M];左子[D]下接[A, A, D]與[F, I, I];右子[O]下接[N, O]與[S]。 - 插入 R 至
[S]形成[R, S]。
- 分裂後:根
- 插入 E:搜尋走右子
[F, I, I]發現滿載,將 I 提升至父節點[D](成為[D, I]),[F, I, I]分裂為[F]與[I]。- 插入 E 至
[F]形成[E, F]。
- 插入 E 至
- 插入 V, E:
- 插入 V 至
[R, S]形成 4-node[R, S, V]。 - 插入 E 至
[E, F]形成 4-node[E, E, F]。
- 插入 V 至
- 插入 R:搜尋至
[R, S, V]發現滿載,將 S 提升至父節點[O](成為[O, S]),[R, S, V]分裂為[R]與[V]。- 插入 R 至
[R]形成[R, R]。
- 插入 R 至
【答案】最終 2-3-4 樹結構如下:
第 3 題
- For an AVL tree, write a C-like pseudo-codes to determine which one of the R-R, L-L, R-L, L-R rotations should be performed. Explain your answer briefly. (25%)
登入後即可作答並保存紀錄。
核心觀念
AVL 樹要求每一個節點都滿足平衡條件。定義節點 的平衡因子為:
對 AVL 樹而言,必須有:
插入新節點後,若某節點 的平衡因子變成 或 ,表示 失衡。此時必須根據「失衡節點 」與「較高子樹的方向」判斷旋轉類型。
四種情況如下:
| 情況 | 失衡方向 | 子樹方向 | 修正方式 |
|---|---|---|---|
| L-L | 左子樹過高 | 左子樹的左側過高 | 對 做右旋 |
| R-R | 右子樹過高 | 右子樹的右側過高 | 對 做左旋 |
| L-R | 左子樹過高 | 左子樹的右側過高 | 先對左子節點左旋,再對 右旋 |
| R-L | 右子樹過高 | 右子樹的左側過高 | 先對右子節點右旋,再對 左旋 |
其中 L、R 表示新節點插入路徑的左、右方向;R-R 並不是「向右旋轉兩次」。
解題方法
先找出插入後第一個失衡的祖先節點 ,計算:
- :左子樹過高,屬於 L 開頭的情況。
- :右子樹過高,屬於 R 開頭的情況。
接著檢查較高子節點的平衡因子,即可判斷第二個方向。
C-like pseudo-code
typedef enum {
LL,
RR,
LR,
RL
} RotationType;
int balanceFactor(Node *v) {
if (v == NULL)
return 0;
return height(v->left) - height(v->right);
}
RotationType determineRotation(Node *z) {
int bfZ = balanceFactor(z);
// 左子樹過高
if (bfZ > 1) {
int bfLeft = balanceFactor(z->left);
if (bfLeft >= 0)
return LL; // L-L
else
return LR; // L-R
}
// 右子樹過高
if (bfZ < -1) {
int bfRight = balanceFactor(z->right);
if (bfRight <= 0)
return RR; // R-R
else
return RL; // R-L
}
return NONE; // 尚未失衡
}
若需直接執行修正,可寫成:
Node *rebalance(Node *z) {
int bfZ = balanceFactor(z);
// L-L:單右旋
if (bfZ > 1 &&
balanceFactor(z->left) >= 0) {
return rotateRight(z);
}
// L-R:先左旋,再右旋
if (bfZ > 1 &&
balanceFactor(z->left) < 0) {
z->left = rotateLeft(z->left);
return rotateRight(z);
}
// R-R:單左旋
if (bfZ < -1 &&
balanceFactor(z->right) <= 0) {
return rotateLeft(z);
}
// R-L:先右旋,再左旋
if (bfZ < -1 &&
balanceFactor(z->right) > 0) {
z->right = rotateRight(z->right);
return rotateLeft(z);
}
return z;
}
四種情況說明
1. L-L 情況
若:
代表 的左子樹過高。若:
表示左子節點的左側不低於右側,失衡路徑為:
因此屬於 L-L,對 做一次右旋即可。
z y
/ \ / \
y D → A z
/ \ / \
A C C D