# 2024q1 Homework2 (quiz1+2) contributed by < `chishuo9810` > ## 第一週測驗題 ### [測驗 1 ](https://hackmd.io/@sysprog/linux2024-quiz1#%E6%B8%AC%E9%A9%97-1) #### 程式運作原理 * 目標 : 以鏈結串列實作非遞廻 `quick sort` * `quick sort` 說明: 1. 選定串列的其中一節點為 `pivot` (此次測驗程式碼令串列最開始為 `pivot` )。 2. 將比 `pivot` 大的節點放置於其右側,比 `pivot` 小的節點放置於其左側。 3. 對 `pivot` 左、右兩側的節點們重複進行前兩點操作,最後即可得到由小到大的排序。 * 重要函式說明 : * `list_length` 回傳串列長度 * `list_add` 將節點插入串列之首 * `list_tail` 找出串列的最後一個節點並回傳 * 舉例: > 初始資料 ```graphviz digraph graphname{ node[shape=box] b2 [label = "2"]; b4 [label = "4"]; b3 [label = "3"]; b5 [label = "5"]; b1 [label = "1"]; node[shape=plaintext] pivot; L [fontcolor="blue"] R [fontcolor="blue"] { rank="same"; b2->b4->b3->b5->b1->NULL; } pivot->b2; L->b2; R->b1; } ``` ``` begin[0] = 2; end[0] = 1; ``` > 第一次迴圈 ```graphviz digraph graphname{ node[shape=box] b2 [label = "2"]; b4 [label = "4"]; b3 [label = "3"]; b5 [label = "5"]; b1 [label = "1"]; node[shape=plaintext] pivot; L [fontcolor="blue"] R [fontcolor="blue"] n1 [label = "NULL"] n2 [label = "NULL"] n3 [label = "NULL"] rankdir=LR; L->b1->n2; R->b5->b3->b4->n3; pivot->b2->n1; } ``` ``` begin[0] = 1; end[0] = 1; begin[1] = 2; end[1] = 2; begin[2] = 5; end[2] = 4; ``` > 第二次迴圈處理右側串列 ```graphviz digraph graphname{ node[shape=box] b4 [label = "4"]; b3 [label = "3"]; b5 [label = "5"]; node[shape=plaintext] pivot; L [fontcolor="blue"] R [fontcolor="blue"] n1 [label = "NULL"] n2 [label = "NULL"] n3 [label = "NULL"] rankdir=LR; R->n3; L->b4->b3->n2 pivot->b5->n1; } ``` ``` begin[0] = 1; end[0] = 1; begin[1] = 2; end[1] = 2; begin[2] = 4; end[2] = 3; begin[3] = 5; end[3] = 5; begin[4] = NULL; end[4] = NULL; ``` > 第三次迴圈 右側串列沒有節點,直接進入下一次迴圈 >第四次迴圈 將節點 5 放入節點 `result` ```graphviz digraph graphname{ node[shape=box] b5 [label = "5"]; node[shape=plaintext] result [fontcolor="red"] rankdir=LR; result->b5->NULL } ``` ``` begin[0] = 1; end[0] = 1; begin[1] = 2; end[1] = 2; begin[2] = 4; end[2] = 3; ``` > 第五次迴圈 ```graphviz digraph graphname{ node[shape=box] b4 [label = "4"]; b3 [label = "3"]; node[shape=plaintext] pivot; L [fontcolor="blue"] R [fontcolor="blue"] n1 [label = "NULL"] n2 [label = "NULL"] n3 [label = "NULL"] rankdir=LR; R->n3; L->b3->n1 pivot->b4->n2; } ``` ``` begin[0] = 1; end[0] = 1; begin[1] = 2; end[1] = 2; begin[2] = 3; end[2] = 3; begin[3] = 4; end[3] = 4; begin[4] = NULL; end[4] = NULL; ``` > 第六次迴圈 右側串列沒有節點,直接進入下一次迴圈 > 第七、八、九、十次迴圈 分別將節點 4、3、2、1 放入節點 `result` ```graphviz digraph graphname{ node[shape=box] b5 [label = "5"]; b4 [label = "4"]; b3 [label = "3"]; b2 [label = "2"]; b1 [label = "1"]; node[shape=plaintext] result [fontcolor="red"] rankdir=LR; result->b1->b2->b3->b4->b5->NULL } ``` ### [測驗 2 ](https://hackmd.io/@sysprog/linux2024-quiz1#%E6%B8%AC%E9%A9%97-2) #### 程式運作原理 * 目標 : 改進合併排序法 * `Timsort` 說明: 在現實世界中,許多資料其實已經部份排序了,`Timsort` 減少了原本合併排序的合併次數 1. 識別出資料中已排序的子序列 `run` 2. 不斷合併這些子序列來達成全部資料的排序 * 重要函式說明 : * `find_run` 從參數傳的節點開始,往後找出最長已排序子串列並回傳。 * `merge_collapse` 檢查是否滿足 : 1. `tp->prev->prev` 的長度要大於 `tp->prev` 和 `tp` 的長度總和。 2. `tp->prev` 的長度要大於 `tp` 的長度。 若不符合條件則進行合併已確保堆疊上的 run 長度保持平衡。 * 舉例: > 初始資料 ```graphviz digraph graphname{ node[shape=box] b1 [label = "1"]; b2 [label = "2"]; b3 [label = "3"]; b4 [label = "4"]; b5 [label = "3"]; b6 [label = "2"]; b7 [label = "4"]; b8 [label = "7"]; b9 [label = "8"]; rankdir=LR; b1->b2->b3->b4->b5->b6->b7->b8->b9->NULL; } ``` >找尋已排序子序列,並將每個子序列的首個節點串接在一起,若遇到子序列順序相反時則自動將其反轉 ```graphviz digraph graphname{ node[shape=box] b1 [label = "1"]; b2 [label = "2"]; b3 [label = "3"]; b4 [label = "4"]; b5 [label = "3"]; b6 [label = "2"]; b7 [label = "4"]; b8 [label = "7"]; b9 [label = "8"]; node[shape=plaintext] n1 [label = "NULL"] n2 [label = "NULL"] n3 [label = "NULL"] rankdir=LR; b7->b8->b9->n3; b6->b5->n2; b1->b2->b3->b4->n1; { rank="same" tp[fontcolor="red"] tp->b7 b7->b6 [label="prev"] b6->b1 [label="prev"] } } ``` >`tp->prev->prev` 的長度並未大於 `tp->prev` 和 `tp` 的長度總和,因此進行合併。 ```graphviz digraph graphname{ node[shape=box] b1 [label = "1"]; b2 [label = "2"]; b3 [label = "3"]; b4 [label = "4"]; b5 [label = "3"]; b6 [label = "2"]; b7 [label = "4"]; b8 [label = "7"]; b9 [label = "8"]; node[shape=plaintext] n1 [label = "NULL"] n3 [label = "NULL"] rankdir=LR; b6->b5->b7->b8->b9->n3; b1->b2->b3->b4->n1; { rank="same" tp[fontcolor="red"] tp->b6 b6->b1 [label="prev"] } } ``` >將最後兩個已排序好的子序列合併後即完成 ```graphviz digraph graphname{ node[shape=box] b1 [label = "1"]; b2 [label = "2"]; b3 [label = "3"]; b4 [label = "4"]; b5 [label = "3"]; b6 [label = "2"]; b7 [label = "4"]; b8 [label = "7"]; b9 [label = "8"]; node[shape=plaintext] n3 [label = "NULL"] rankdir=LR; b1->b2->b6->b3->b5->b4->b7->b8->b9->n3; } ``` >相較合併排序法原本需要進行 8 次的合併,這筆資料由於有部份排序過,透過 `Timsort` 僅僅進行了 2 次合併 ## 第二週測驗題 ### [測驗 1 ](https://hackmd.io/@sysprog/linux2024-quiz2#%E6%B8%AC%E9%A9%97-1) #### 程式運作原理 * 目標 : 透過遞迴呼叫的方式,利用深度搜尋法重建二元樹。 * 說明: 1. 由前序得知根節點。 2. 在中序中尋得根節點,左側即為左子樹,右側即為右子數。 3. 左子樹和右子樹接著進行 1、2。 4. 反覆操作 1~3 即可重建二元樹 * 重要函式說明 : * `dfs`回傳已建成之子樹的根節點 * `find`回傳根節點在中序的位置 ### [測驗 2 ](https://hackmd.io/@sysprog/linux2024-quiz2#%E6%B8%AC%E9%A9%97-2) ### [測驗 3 ](https://hackmd.io/@sysprog/linux2024-quiz2#%E6%B8%AC%E9%A9%97-3)