# 2023q1 Homework (quiz1) contributed by < terry23304 > ## 測驗 1 ### 運作原理 ```graphviz digraph pic1 { rankdir = LR; {4->9->2->5->8}; } ``` 取第一個 entry 當`pivot`,並把`pivot`移除串列 ```c struct item *pivot = list_first_entry(head, item, list); list_del(&pivot->list); ``` ```graphviz digraph pic1 { rankdir = LR; {rank = 1 9->2->5->8} {rank = 2 pivot->4}; } ``` 使用 `list_for_each_entry_safe` 迭代,當節點值小於 pivot 就用 `list_move_tail` 把節點移到 `list_less` 的最後,大於則移到 `list_greater` 的最後 ```c list_for_each_entry_safe (itm, is, head, list) { if (cmpint(&itm->i, &pivot->i) < 0) list_move_tail (&itm->list, &list_less); else list_move_tail (&itm->list, &list_greater); } ``` ```graphviz digraph pic1 { rankdir = LR; {rank = 1 greater->9->5->8} {rank = 2 less->2}; {rank = 3 pivot->4}; } ``` 並遞迴執行直到 `list_less` 與 `list_greater` 排序完成 ```c list_sort(&list_less); list_sort(&list_greater); ``` ```graphviz digraph pic1 { rankdir = LR; {rank = 1 greater->8} {rank = 2 less->5}; {rank = 3 pivot->9}; } ``` 再將 pivot 加回串列中,並用 `list_splice` 將 `list_less` 加到 head 後, pivot 之前再執行 `list_splice_tail` 將 `list_greater` 加回結尾完成排序。 ```c list_add(&pivot->list, head); list_splice(&list_less, head); list_splice_tail(&list_greater, head); ``` ```graphviz digraph pic1 { rankdir = LR; {rank = 1 head->2->4} } ``` ```graphviz digraph pic1 { rankdir = LR; {rank = 1 head->2->4->5->8->9} } ``` - [ ] 針對 Quick sort 提出上述程式碼的改進方案並實作 - [ ] 引入上述的 hybrid sort 策略並在 Linux 核心風格的 circular doubly-linked list 實作,需要量化性能表現並探討不同資料集 (data set) 的影響 ## 測驗 2 ### 運作原理 用 stack 來模擬遞迴 一開始先將要排序的串列移到 stack[0],並把 head 變成 NULL queue,用`top`紀錄 stack 位置 ```c int top = -1; list_splice_init(head, &stack[++top]); ``` 每次迴圈將 stack 最上層的 queue 移到`partition list`中。 ```c INIT_LIST_HEAD(&partition); list_splice_init(&stack[top--], &partition); ``` - `partition` 若長度大於一則表示子串列尚未完成排序,將`partition list`第一個 entry 移出當成 pivot ,迭代 partition list 小於 pivot 的點移到 `list_less` 的開頭,大於等於則移到 `list_greater` 的開頭 ```c struct item *pivot = list_first_entry(&partition, struct item, list); list_del(&pivot->list); INIT_LIST_HEAD(&pivot->list); struct item *itm = NULL, *is = NULL; list_for_each_entry_safe (itm, is, &partition, list) { list_del(&itm->list); if (cmpint(&itm->i, &pivot->i) < 0) list_move(&itm->list, &list_less); else list_move(&itm->list, &list_greater); } ``` 最後把 pivot 放到 `list_less` 的結尾,若`list_greater`非空則 push 到 stack ,再檢查`list_less`,若非空則 push 到 stack ```c list_move_tail (&pivot->list, &list_less); if (!list_empty(&list_greater)) list_splice_tail(&list_greater, &stack[++top]); if (!list_empty(&list_less)) list_splice_tail(&list_less, &stack[++top]); ``` - 若`partition`長度小於等於一則表示子串列完成排序,把`partition` push 到 stack(感覺不需要),並把節點移到 head 尾(stack 最小值在最上面),合併完後就排序完成。 ```c top++; list_splice_tail(&partition, &stack[top]); while (top >= 0 && list_is_singular(&stack[top])) { struct item *tmp = list_first_entry(&stack[top], struct item, list); list_del(&tmp->list); INIT_LIST_HEAD(&stack[top--]); list_add_tail(&tmp->list, head); } ``` ### 設計、實作缺失 以下四行可換成 `list_splice_tail_init(&stack[top], head)` ```c struct item *tmp = list_first_entry(&stack[top], struct item, list); list_del(&tmp->list); INIT_LIST_HEAD(&stack[top--]); list_add_tail(&tmp->list, head); ``` - [ ] 比較測驗 1 和測驗 2 的程式碼,設計實驗來分析二者效能,解釋為何非遞迴的實作未能總是比遞迴的實作快 - [ ] 提出效能改進方案,特別是避免依賴 MAX_DEPTH - [ ] 引入 Introsort,也就是 quick sort 和 heap sort 當 quicksort 迭代 $2∗log(n)$ 次後還排序完成,那就很可能會得到 $O(n^2)$