# 2024q1 Homework2 (quiz1+2) contributed by < [`Randy-Chuang`](https://github.com/Randy-Chuang) > ## 2024q1 [第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1) ### 測驗 `1` 先參考 [Optimized QuickSort — C Implementation (Non-Recursive)](https://alienryderflex.com/quicksort/) ,以下我把程式碼片段貼入解釋理解過程: ```c i = 0; beg[0]=0; end[0]=elements; while (i>=0) { L=beg[i]; R=end[i]-1; if (L<R) { // 取區段 (partition) 第一個 element 做 pivot 來比較 piv=arr[L]; if (i==MAX_LEVELS-1) return NO; while (L<R) { /// 從區段頭尾比較,根據 pivot 將區段分為小大兩邊 while (arr[R]>=piv && L<R) R--; if (L<R) arr[L++]=arr[R]; while (arr[L]<=piv && L<R) L++; if (L<R) arr[R--]=arr[L]; } // 將區端分為小大兩個子區段 arr[L]=piv; beg[i+1]=L+1; end[i+1]=end[i]; end[i++]=L; } else { i--; // L < R 代表區段排序尚未結束,持續減少讓迴圈運作到結束 } } ``` 範例顯示外層迴圈第一輪結束後的結果: ![image](https://hackmd.io/_uploads/ByZA22Tpa.png) 外層迴圈跑完第一輪陣列 `beg[]` 和 `end[]` 內數值的變化: | 陣列 \ index | 0 | 1 | 2 | ... | | ------------ | ----------------- | ----------------- | --- | --- | | `beg[]` | 0 $\rightarrow$ 0 | 0 $\rightarrow$ 4 | 0 | 0 | | `end[]` | 8 $\rightarrow$ 3 | 0 $\rightarrow$ 8 | 0 | 0 | 配上範例能發現排序過程中 pivot 變數被用來將大小區段分開後,再改變 `beg[]` 和 `end[]` 來紀錄目前 Quick Sort 待排序的區段,以每次迴圈迭代而更改的陣列內容當作分枝來看,就能看出 Quick Sort 遞迴樹的變化,行為與 [Iterative DFS](https://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search) 中佇列的控制相同。每跑一次迴圈,當次選用的 pivot 就會被擺到左區塊最後,因此左邊區段最後的元素剛好不用再排序 (圖中紅色標示) 。 ```graphviz digraph g { label="Quick Sort Recursion Tree"; node [shape = record,height=.1]; node0[label = "[0, 8)"]; node1[label = <[0, <FONT COLOR="red">3</FONT>)>]; node2[label = "[4, 8)"]; node3[label = ""]; node4[label = ""]; node5[label = ""]; node6[label = ""]; "node0" -> "node1"; "node0" -> "node2"; "node1" -> "node3"; "node1" -> "node4"; "node2" -> "node5"; "node2" -> "node6"; } ``` 但是待排序陣列若是排列特殊會讓遞迴樹產生傾斜,極端情況會讓分配不均的小區段一直保留在 `beg[]` 和 `end[]` 陣列前端,造成長度不足以紀錄遞迴樹的分枝,因此作者改善程式碼確保迭代過程中優先處理較小的區段,遞迴樹的高度不會超過 $\lceil {Log_2(n)} \rceil$ 。 ```graphviz digraph g { label="Quick Sort Tilted Recursion Tree"; node [shape = record,height=.1]; node0[label = "[0, n)"]; node1[label = "[0, 1)"]; node2[label = "[2, n)"]; node3[label = "..."]; node4[label = "..."]; node5[label = "[2, 3)"]; node6[label = "[4, n)"]; node7[label = "[6, n)"]; node8[label = "[8, n)"]; "node0" -> "node1"; "node0" -> "node2"; "node2" -> "node5"; "node2" -> "node6"; "node6" -> "node7"; "node6" -> "node3"; "node7" -> "node8"; "node7" -> "node4"; } ``` ```diff arr[L]=piv; beg[i+1]=L+1; end[i+1]=end[i]; end[i++]=L; + if (end[i]-beg[i] > end[i-1]-beg[i-1]) { + swap=beg[i]; beg[i]=beg[i-1]; beg[i-1]=swap; + swap=end[i]; end[i]=end[i-1]; end[i-1]=swap; + } } else { i--; } ``` #### 測驗程式碼 (*left)->next (*left)->next p->next list_tail(&left) list_tail(&right) :::warning 延伸問題: 解釋上述程式碼的運作原理,提出改進方案並予以實作。 使用 Linux 核心風格的 List API 改寫上述程式碼,並針對鏈結串列,提出可避免最差狀況的快速排序實作,應設計效能評比的測試程式。 ::: ### 測驗 `2` :::warning 延伸問題: 解釋上述程式碼的運作原理,提出改進方案並予以實作。 將改進過的 Timsort 實作整合進 sysprog21/lab0-c,作為另一個有效的排序命令,應設計效能評比的測試程式,確保測試案例符合 Timsort 設想的資料分布樣式。 ::: ## 2024q1 [第 2 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz2) ### 測驗 `1` ### 測驗 `2` ### 測驗 `3` ## 表單測驗 ## 參考資料 - Graphviz - [Graphviz Doc](https://graphviz.org/documentation/) / [Layout Engines](https://graphviz.org/docs/layouts/) / [Gallery](https://www.graphviz.org/gallery/) / [Drawing graphs with dot](https://graphviz.org/pdf/dotguide.pdf) - [Multiple graphs inside Graphviz DOT file](https://stackoverflow.com/questions/63869676/multiple-graphs-inside-graphviz-dot-file)