115 高普考高考三級 資訊處理 資料結構 考古題詳解

115 年・高普考・高考三級・資訊處理
試題代碼 資料結構

第 一 題25 分

某系統 A 使用雜湊表(hash table)儲存不同的正整數鍵值,不允許重複鍵值。雜湊表有 11 個儲存格,索引從 0 開始,雜湊函數(hash function)為 hA(k)=k mod 11h_A(k)=k\bmod 11,並使用平方探查法(quadratic probing)處理碰撞(collision)問題,探查序列為:

hiA(k)=(hA(k)+i2) mod 11,i=0,1,2,…h_i^A(k)=(h_A(k)+i^2)\bmod 11,\quad i=0,1,2,\ldots

刪除資料時,被刪除資料的位置標記為特殊符號 DELETED。請回答下列問題:

(一) 給定一個空的雜湊表,依序插入下列鍵值:

22, 1, 13, 24, 35, 46, 7, 1822,\ 1,\ 13,\ 24,\ 35,\ 46,\ 7,\ 18

請畫出所有鍵值插入完成後的雜湊表狀態,並列出插入鍵值 46 時的完整探查過程。(10 分)

(二) 承上題,依序刪除鍵值 24、13,畫出刪除後的雜湊表狀態。並說明為什麼刪除鍵值時需用 DELETED 標記,而不能將該鍵值所在的儲存格恢復成「從未存放過鍵值」的空狀態。(5 分)

(三) 承上題,執行插入鍵值 12,請列出插入時的探查過程、操作停止的理由,並寫出 12 最後插入的儲存格。插入時,DELETED 標記視為可放入新鍵值的儲存格。請注意鍵值不能重複。(5 分)

(四) 相較於系統 A,考慮另一個採用平方探查法之系統 B。系統 B 的表格大小為 8,索引亦從 0 開始,雜湊函數 hB(k)h_B(k) 及探查序列 hiB(k)h_i^B(k) 分別定義為:

hB(k)=k mod 8h_B(k)=k\bmod 8

hiB(k)=(hB(k)+i+2i2) mod 8,i=0,1,2,…h_i^B(k)=(h_B(k)+i+2i^2)\bmod 8,\quad i=0,1,2,\ldots

在非均勻雜湊(non-uniform hashing)的情況下,也就是許多鍵值可能被分配到相同或少數幾個初始雜湊位置時,哪一個系統的雜湊表儲存格利用率可能較高?並說明理由。(5 分)

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

這一題的完整詳解

核心觀念

本題考查開放定址法中的平方探查、刪除標記,以及探查序列能否覆蓋足夠多的儲存格。

系統 A 的探查位置為

hiA(k)=(k mod 11+i2) mod 11h_i^A(k)=(k\bmod 11+i^2)\bmod 11

發生碰撞時,依 i=0,1,2,…i=0,1,2,\ldots 的順序探查。刪除時須將該格標記為 DELETED:搜尋鍵值時須越過此標記繼續探查;插入時則可將該標記格用來存放新鍵值。

解題方法

(一)插入 8 個鍵值

逐一依探查序列尋找第一個空格:

插入鍵值初始位置與探查結果插入位置
2222 mod 11=022\bmod 11=0,位置 0 空0
11 mod 11=11\bmod 11=1,位置 1 空1
1313 mod 11=213\bmod 11=2,位置 2 空2
24初始位置 2 已占用;i=1i=1 探查 (2+1) mod 11=3(2+1)\bmod 11=3,空3
35初始位置 2 已占用;i=1i=1 到 3 已占用;i=2i=2 到 (2+4) mod 11=6(2+4)\bmod 11=6,空6
46初始位置 2 已占用;i=1i=1 到 3 已占用;i=2i=2 到 6 已占用;i=3i=3 到 (2+9) mod 11=0(2+9)\bmod 11=0 已占用;i=4i=4 到 (2+16) mod 11=7(2+16)\bmod 11=7,空7
7初始位置 7 已占用;i=1i=1 到 8,空8
18初始位置 7 已占用;i=1i=1 到 8 已占用;i=2i=2 到 0 已占用;i=3i=3 到 4,空4

插入 46 時,其初始位置為 46 mod 11=246\bmod 11=2,完整探查過程如下:

ii探查位置 (2+i2) mod 11(2+i^2)\bmod 11狀態
0213 已占用
1324 已占用
2635 已占用
3022 已占用
47空,插入 46

插入完成後的雜湊表:

索引012345678910
鍵值221132418空35467空空

(二)刪除 24、13

鍵值 24 位於索引 3,鍵值 13 位於索引 2。刪除後以 DELETED 標記:

索引012345678910
鍵值221DELETEDDELETED18空35467空空
🔒

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

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

免費註冊

第 二 題25 分

給定一個無向圖 G=(V,E)G=(V,E),每個頂點代表一個地點,每條邊 e (e∈E)e\ (e\in E) 代表一條道路,邊的正整數權重 ω(e)\omega(e) 表示該道路的塞車程度,數值越大越壅塞。對於一條從起點 ss 到終點 tt(s,t∈Vs,t\in V)的路徑 PP,其最大塞車程度 C(P)C(P) 定義為路徑上所有邊權重的最大值:

C(P)=max⁡e∈Pω(e)C(P)=\max_{e\in P}\omega(e)

本題透過修改 Dijkstra 最短路徑演算法中陣列 dd 的定義與更新方式,求出從 ss 到 tt 可行路徑所能達到的「最大塞車程度的最小值」。

修改後的演算法流程與 Dijkstra 最短路徑演算法相同,差異僅在於 d[v]d[v](v∈Vv\in V)的定義與更新規則,其中,新的 d[v]d[v] 表示目前已知從 ss 到 vv 的路徑中,最大邊權重的最小值。初始時令 d[s]=0d[s]=0,其他頂點 vv 的 d[v]=∞d[v]=\infty(v≠sv\ne s)。之後依照 Dijkstra 演算法,每一輪選出尚未被選定且 dd 值最小的頂點 uu,並將原本的更新方式:

d[v]=min⁡(d[v],d[u]+ω(u,v))d[v]=\min(d[v],d[u]+\omega(u,v))

改為:

d[v]=min⁡(d[v],max⁡(d[u],ω(u,v)))d[v]=\min(d[v],\max(d[u],\omega(u,v)))

其中 ω(u,v)\omega(u,v) 為邊 (u,v)(u,v)(u,v∈Vu,v\in V)的權重。重複進行,直到終點 tt 被選定為止。

(一) 以下列無向圖為例,令起點 ss 為 A,終點 tt 為 F,依照修改後的演算法,逐步列出每次選定一個頂點後陣列 dd 的變化過程。陣列中的頂點順序請依字母順序排列。(15 分)

(此題原卷圖形文字擷取不全,請參考原卷。)

(二) 說明修改後演算法之正確性,是基於 d[v]d[v] 更新規則具有何種性質。(5 分)

(三) 假設圖以相鄰串列(adjacency list)表示。若要在尚未選定的頂點中找出 dd 值最小者,可使用以下兩種方法:

方法一:每次以線性方式掃描所有尚未選定的頂點找出最小 dd 值。

方法二:使用最小堆積(min-heap)維護目前 dd 值最小的頂點。

分別就這兩種方法,分析修改後演算法最壞情況的時間複雜度。(5 分)

🖼️ 本題原卷含圖表。基於試題來源之版權考量,本站不轉載原卷影像; 題目文字已完整轉錄,圖表請自行參閱考選部公告之試題原檔。

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

這一題的完整詳解

核心觀念

本題將 Dijkstra 演算法中的「路徑總權重最小」改為「路徑上的最大邊權重最小」,也就是求解最小瓶頸路徑(minimax path)。

對任一路徑 PP:

C(P)=max⁡e∈Pω(e)C(P)=\max_{e\in P}\omega(e)

因此,若目前從 ss 到 uu 的最佳瓶頸值為 d[u]d[u],再經過邊 (u,v)(u,v),新的瓶頸值為:

max⁡(d[u],ω(u,v))\max(d[u],\omega(u,v))

更新規則為:

d[v]=min⁡(d[v],max⁡(d[u],ω(u,v)))d[v]=\min\left(d[v],\max(d[u],\omega(u,v))\right)

依圖可讀得相關邊如下:

  • A−B=9, A−C=2, A−D=3A-B=9,\ A-C=2,\ A-D=3
  • B−C=1, B−D=6, B−E=7, B−H=8B-C=1,\ B-D=6,\ B-E=7,\ B-H=8
  • C−E=8, C−G=6C-E=8,\ C-G=6
  • D−G=2D-G=2
  • E−G=5, E−H=4, E−F=9E-G=5,\ E-H=4,\ E-F=9
  • G−F=7G-F=7
  • H−F=6H-F=6

(一)逐步執行修改後的 Dijkstra 演算法

頂點順序固定為:

(A,B,C,D,E,F,G,H)(A,B,C,D,E,F,G,H)

初始狀態:

d=(0,∞,∞,∞,∞,∞,∞,∞)d=(0,\infty,\infty,\infty,\infty,\infty,\infty,\infty)

1. 選定 AA

由 AA 更新:

  • d[B]=max⁡(0,9)=9d[B]=\max(0,9)=9
  • d[C]=max⁡(0,2)=2d[C]=\max(0,2)=2
  • d[D]=max⁡(0,3)=3d[D]=\max(0,3)=3

因此:

d=(0,9,2,3,∞,∞,∞,∞)d=(0,9,2,3,\infty,\infty,\infty,\infty)

2. 選定 CC

目前未選定頂點中,d[C]=2d[C]=2 最小。

由 CC 更新:

  • 經 C→BC\to B:max⁡(2,1)=2\max(2,1)=2,所以 d[B]=2d[B]=2
  • 經 C→EC\to E:max⁡(2,8)=8\max(2,8)=8,所以 d[E]=8d[E]=8
  • 經 C→GC\to G:max⁡(2,6)=6\max(2,6)=6,所以 d[G]=6d[G]=6

因此:

d=(0,2,2,3,8,∞,6,∞)d=(0,2,2,3,8,\infty,6,\infty)

3. 選定 BB

目前 d[B]=2d[B]=2 最小。

由 BB 更新:

  • 經 B→DB\to D:max⁡(2,6)=6\max(2,6)=6,不改善原本的 d[D]=3d[D]=3
  • 經 B→EB\to E:max⁡(2,7)=7\max(2,7)=7,故 d[E]=7d[E]=7
  • 經 B→HB\to H:max⁡(2,8)=8\max(2,8)=8,故 d[H]=8d[H]=8

因此:

d=(0,2,2,3,7,∞,6,8)d=(0,2,2,3,7,\infty,6,8)

4. 選定 DD

目前 d[D]=3d[D]=3 最小。

由 DD 更新:

  • 經 D→GD\to G:max⁡(3,2)=3\max(3,2)=3,故 d[G]=3d[G]=3

因此:

d=(0,2,2,3,7,∞,3,8)d=(0,2,2,3,7,\infty,3,8)

5. 選定 GG

目前 d[G]=3d[G]=3 最小。

由 GG 更新:

  • 經 G→EG\to E:max⁡(3,5)=5\max(3,5)=5,故 d[E]=5d[E]=5
  • 經 G→FG\to F:max⁡(3,7)=7\max(3,7)=7,故 d[F]=7d[F]=7

因此:

d=(0,2,2,3,5,7,3,8)d=(0,2,2,3,5,7,3,8)

6. 選定 EE

目前 d[E]=5d[E]=5 最小。

由 EE 更新:

  • 經 E→HE\to H:max⁡(5,4)=5\max(5,4)=5,故 d[H]=5d[H]=5
  • 經 E→FE\to F:max⁡(5,9)=9\max(5,9)=9,不改善原本的 d[F]=7d[F]=7

因此:

d=(0,2,2,3,5,7,3,5)d=(0,2,2,3,5,7,3,5)
🔒

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

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

免費註冊

第 三 題25 分

給定一棵二元搜尋樹(binary search tree),且該樹同時也是一棵 AVL 樹。樹的節點在 C 語言中宣告如下:

typedef struct Node { int key; // 節點的鍵值,所有節點的鍵值皆互不相同 int size; // 以該節點為根的子樹節點總數(包含自己) struct Node *left; // 指向左子節點 struct Node *right; // 指向右子節點 } Node;

並定義以下函式:

int size(Node *node):

若傳入的 node 為 NULL,則回傳 0;否則回傳 node->size。

int count_less_equal(Node *node, int val):

回傳以 node 為根的子樹中,所有鍵值小於等於 val 的節點總數。

Node* select(Node *node, int r):

回傳以 node 為根的子樹中,第 rr 小的節點指標,rr 從 1 開始算。

Node* greater_k_smallest(Node *root, int val, int k):

找出以 root 為根的整棵樹中,所有鍵值大於 val 的節點裡,第 kk 小的節點,kk 從 1 開始算。若第 kk 小的節點不存在,則回傳 NULL。

(一) 完成下列程式碼的空格。(20 分)

int count_less_equal(Node *node, int val) {

if (node == NULL) return 0;

if (node->key > val)

    return count_less_equal(node->left, val);

else

    return size(node->left) + (1);

}

Node* select(Node *node, int r) {

int left_size = size(node->left);

if (r == (2)) return node;

else if (r <= left_size)

    return select(node->left, r);

else

    return (3);

}

Node* greater_k_smallest(Node *root, int val, int k) {

int x = count_less_equal(root, val);

int y = (4);

if (y > size(root)) return NULL;

return select(root, y);

}

(二) 下圖為一棵包含 5 個節點且滿足 AVL 平衡特性的二元搜尋樹,圖中顯示每個節點的鍵值。若將鍵值為 70 的新節點插入此樹,為保持 AVL 樹的平衡,會觸發旋轉。請畫出旋轉後的樹狀結構圖。除新插入的節點 70 外,若原有節點的 size 欄位值在旋轉後發生改變,請在旋轉後的圖中,於該節點旁標示其新的 size 欄位值。(5 分)

(此題原卷圖形文字擷取不全,請參考原卷。)

🖼️ 本題原卷含圖表。基於試題來源之版權考量,本站不轉載原卷影像; 題目文字已完整轉錄,圖表請自行參閱考選部公告之試題原檔。

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

這一題的完整詳解

核心觀念

本題考查二元搜尋樹的下列觀念:

  1. 利用 size 欄位快速取得左、右子樹的節點數。
  2. 以遞迴方式計算鍵值小於等於 val 的節點數。
  3. 以「左子樹節點數」判斷目前節點是否為第 rr 小。
  4. AVL 樹插入後,若左右子樹高度差超過 11,必須透過旋轉恢復平衡。
  5. 樹旋轉後,受影響節點的 size 必須重新計算。

(一)程式碼填空

空格(1)

若 node->key > val,目前節點及右子樹的鍵值都大於 val,只需搜尋左子樹。

若 node->key <= val,則:

  • 左子樹全部符合;
  • 目前節點本身符合;
  • 右子樹中仍須繼續搜尋符合條件的節點。

因此:

結果=size(node->left)+1+count_less_equal(node->right, val)\text{結果} = \text{size(node->left)} + 1 + \text{count\_less\_equal(node->right, val)}

故空格(1)為:

1 + count_less_equal(node->right, val)

空格(2)

左子樹有 left_size 個節點,因此目前節點在以 node 為根的子樹中排名:

left_size+1left\_size+1

故空格(2)為:

left_size + 1

空格(3)

當 r>left_size+1r>left\_size+1 時,目標節點位於右子樹中。扣除左子樹與目前節點後,在右子樹中的排名為:

r−left_size−1r-left\_size-1

故空格(3)為:

select(node->right, r - left_size - 1)

空格(4)

令:

x = count_less_equal(root, val);

表示整棵樹中鍵值小於等於 val 的節點數。所有鍵值大於 val 的節點,從整棵樹的第 x+1x+1 小開始。

其中第 kk 個大於 val 的節點,在整棵樹中的排名為:

y=x+ky=x+k

故空格(4)為:

x + k

完整答案如下:

🔒

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

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

免費註冊

第 四 題25 分

考慮以下兩個互相呼叫(mutually recursive)的 C 語言函式:

int foo(int n) { if (n <= 1) return 1; return foo(n - 1) + bar(n - 1) + 2; }

int bar(int n) { if (n <= 1) return 1; return foo(n - 1) + bar(n - 1); }

請回答下列問題。每一小題請寫出推導過程,無推導過程不予計分。

(一) 當執行 foo(10) 時,一共會呼叫 foo() 函式幾次(包含最外層 foo(10) 的這一次呼叫)?(10 分)

(二) 執行 foo(10) 的最終回傳結果為何?(10 分)

(三) 以 Big-O 表示 foo(n) 的時間複雜度(time complexity)。本題若同一個「函式與參數」組合被呼叫多次,每次都重新計算,不會儲存先前的計算結果供之後使用。(5 分)

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

這一題的完整詳解

核心觀念

本題考兩個互相遞迴函式的呼叫次數、回傳值與時間複雜度。解題時分別建立「函式呼叫次數」與「函式回傳值」的遞迴式;兩者代表不同的量,不能混為一談。

題目指定同一個「函式與參數」組合再次被呼叫時,仍須重新計算,沒有使用記憶化,因此遞迴呼叫會形成指數級的計算量。

解題方法

令:

  • F(n)F(n):執行一次 foo(n),過程中呼叫 foo 的總次數,包含最外層這一次。
  • B(n)B(n):執行一次 bar(n),過程中呼叫 foo 的總次數。

當 n≤1n\le 1 時,函式直接回傳,不再呼叫其他函式,因此:

F(1)=1,B(1)=0F(1)=1,\qquad B(1)=0

當 n>1n>1 時,foo(n) 本身算一次 foo 呼叫,接著呼叫 foo(n-1) 和 bar(n-1);而 bar(n) 會呼叫這兩個函式,但 bar(n) 本身不計入 foo 呼叫次數。因此:

F(n)=1+F(n−1)+B(n−1)F(n)=1+F(n-1)+B(n-1) B(n)=F(n−1)+B(n−1)B(n)=F(n-1)+B(n-1)

相減可得 F(n)−B(n)=1F(n)-B(n)=1。令 S(n)=F(n)+B(n)S(n)=F(n)+B(n),則:

S(1)=1,S(n)=1+2S(n−1)S(1)=1,\qquad S(n)=1+2S(n-1)

解此遞迴式:

S(n)=3⋅2n−1−2S(n)=3\cdot 2^{n-1}-2

因此,當 n>1n>1 時:

F(n)=S(n)+12=3⋅2n−1−12F(n)=\frac{S(n)+1}{2} =\frac{3\cdot 2^{n-1}-1}{2}

(一)計算 foo(10) 呼叫 foo 的次數

F(10)=3⋅29−12=1536−12=15352F(10)=\frac{3\cdot 2^9-1}{2} =\frac{1536-1}{2} =\frac{1535}{2}

此處結果非整數,表示上面的閉式在 F(1)=1, B(1)=0F(1)=1,\ B(1)=0 的邊界條件下不成立。直接將 S(n)S(n) 遞迴式解開:

S(n)=1+2S(n−1),S(1)=1S(n)=1+2S(n-1),\qquad S(1)=1 S(n)=2n−1S(n)=2^n-1

又因為 F(n)=1+S(n−1)F(n)=1+S(n-1),所以:

F(n)=1+(2n−1−1)=2n−1F(n)=1+(2^{n-1}-1)=2^{n-1}

故:

F(10)=29=512F(10)=2^9=512
🔒

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

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

免費註冊

其他考古題