# 2021q1 Homework1 (quiz1) contributed by < `yellow-hank` > ###### tags: `LinuxKernel` ## Linked list 結構 ```cpp typedef struct __node { int value; struct __node *next; } node_t; ``` - 結構示意圖 ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> value|<f1> next\n}"]; } ``` ## Quick sort recursive 實作 ```cpp static inline void list_add_node_t(node_t **list, node_t *node_t) { node_t->next = *list; *list = node_t; } static inline void list_concat(node_t **left, node_t *right) { while (*left) left = &((*left)->next); *left = right; } void quicksort(node_t **list) { if (!*list) return; node_t *pivot = *list; int value = pivot->value; node_t *p = pivot->next; pivot->next = NULL; node_t *left = NULL, *right = NULL; while (p) { node_t *n = p; p = p->next; list_add_node_t(n->value > value ? &right : &left, n); } quicksort(&left); quicksort(&right); node_t *result = NULL; list_concat(&result, left); list_concat(&result, pivot); list_concat(&result, right); *list = result; } ``` ### 執行示意圖 - 原始排序 ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> 3|<f1> next\n}"]; struct2 [label="{<f0> 5|<f1> next\n}"]; struct3 [label="{<f0> 1|<f1> next\n}"]; struct4 [label="{<f0> 4|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{list}"]; struct6 -> struct1:f0; struct1:f1 -> struct2:f0; struct2:f1 -> struct3:f0; struct3:f1 -> struct4:f0; struct4:f1 -> struct5:f0; } ``` - 選擇第一個當作 pivot ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> 3|<f1> next\n}"]; struct2 [label="{<f0> 5|<f1> next\n}"]; struct3 [label="{<f0> 1|<f1> next\n}"]; struct4 [label="{<f0> 4|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{list}"]; struct7 [label="{pivot}"]; struct1:f1 -> struct2:f0; struct2:f1 -> struct3:f0; struct3:f1 -> struct4:f0; struct4:f1 -> struct5:f0; struct6 -> struct1:f0; struct7 -> struct1:f0; } ``` - 走訪每個節點把大於 pivot 放入 right 裡,小於的放入 left 中 ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> 3|<f1> next\n}"]; struct2 [label="{<f0> 5|<f1> next\n}"]; struct3 [label="{<f0> 1|<f1> next\n}"]; struct4 [label="{<f0> 4|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{left}"]; struct7 [label="{right}"]; struct8 [label="{pivot}"]; struct2:f1 -> struct4:f0; struct3:f1 -> struct5:f0; struct6 -> struct3:f0; struct7 -> struct2:f0; struct8 -> struct1:f0; } ``` - 繼續排序 right 和 left - 先顯示 left 的部分 , pivot 再次指向第一個值 ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 1|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{list}"]; struct8 [label="{pivot}"]; struct3:f1 -> struct5:f0; struct6 -> struct3:f0; struct8 -> struct3:f0; } ``` - 走訪每個節點把大於 pivot 放入 right 裡,小於的放入 left 中 ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 1|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct8 [label="{pivot}"]; struct6 [label="{left}"]; struct7 [label="{right}"]; struct8 -> struct3:f0; struct7 -> struct5:f0; } ``` - 進行合併,依序放入 left, pivot, right ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 1|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct8 [label="{list}"]; struct6 [label="{left}"]; struct7 [label="{right}"]; struct9 [label="{pivot}"]; struct8 -> struct3:f0; struct7 -> struct5:f0; struct3 -> struct5:f0; struct9 -> struct3:f0; } ``` - 顯示 right 的部分 ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 5|<f1> next\n}"]; struct5 [label="{<f0> 4|<f1> next\n}"]; struct6 [label="{list}"]; struct8 [label="{pivot}"]; struct3:f1 -> struct5:f0; struct6 -> struct3:f0; struct8 -> struct3:f0; } ``` - 走訪每個節點把大於 pivot 放入 right 裡,小於的放入 left 中 ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 5|<f1> next\n}"]; struct5 [label="{<f0> 4|<f1> next\n}"]; struct8 [label="{pivot}"]; struct6 [label="{right}"]; struct7 [label="{left}"]; struct8 -> struct3:f0; struct7 -> struct5:f0; } ``` - 進行合併,依序放入 left, pivot, right ```graphviz digraph structs { node[shape=record] struct3 [label="{<f0> 4|<f1> next\n}"]; struct5 [label="{<f0> 5|<f1> next\n}"]; struct8 [label="{list}"]; struct6 [label="{right}"]; struct7 [label="{left}"]; struct9 [label="{pivot}"]; struct8 -> struct3:f0; struct7 -> struct3:f0; struct3 -> struct5:f0; struct9 -> struct5:f0; } ``` - 兩個 quick sort 回傳後的結果 ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> 3|<f1> next\n}"]; struct2 [label="{<f0> 4|<f1> next\n}"]; struct3 [label="{<f0> 1|<f1> next\n}"]; struct4 [label="{<f0> 5|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{left}"]; struct7 [label="{right}"]; struct8 [label="{pivot}"]; struct2:f1 -> struct4:f0; struct3:f1 -> struct5:f0; struct6 -> struct3:f0; struct7 -> struct2:f0; struct8 -> struct1:f0; } ``` - 進行合併,依序放入 left, pivot, right ```graphviz digraph structs { node[shape=record] struct1 [label="{<f0> 3|<f1> next\n}"]; struct2 [label="{<f0> 4|<f1> next\n}"]; struct3 [label="{<f0> 1|<f1> next\n}"]; struct4 [label="{<f0> 5|<f1> next\n}"]; struct5 [label="{<f0> 2|<f1> next\n}"]; struct6 [label="{left}"]; struct7 [label="{right}"]; struct8 [label="{pivot}"]; struct9 [label="{list}"]; struct2:f1 -> struct4:f0; struct3:f1 -> struct5:f0; struct5:f1 -> struct1:f0; struct1:f1 -> struct2:f0; struct6 -> struct3:f0; struct7 -> struct2:f0; struct8 -> struct1:f0; struct9 -> struct3:f0; } ``` - 完成排序 ## 使用其他 Pseudorandom number generator - 原本的 quick sort 是使用 random 函式產生亂數,但是多執行幾次就會發現產生的亂數都一樣。 - 擷取部分程式碼 ```cpp while (count--) list = list_make_node_t(list, random() % 1024); ``` - 後來選擇使用 srand 函式來產生亂數 ```cpp srand(time(NULL)); while (count--) list = list_make_node_t(list, rand() % 1024); ``` - 以現在的時刻當作 seed 來產生亂數,但是這種方式會讓人有疑惑,亂數的因子裡面還有一個時間,所以還有改善的空間。 ## non-recursive quick sort 實作 ```cpp void nr_quicksort(node_t **list) { #define MAX_LEVELS 1000 //list is empty or only one in it if(!*list || !(*list)->next) return; int top;//for stack node_t *stack[MAX_LEVELS]; node_t *result = NULL; node_t *pivot ; stack[0] = (*list); while(top>=0 && top < MAX_LEVELS) { if(!stack[top]) { top--; continue; } if(!stack[top]->next) { stack[top]->next = result; result = stack[top]; top--; continue; } pivot = stack[top]; node_t *n = pivot->next; int value = pivot->value; pivot->next = NULL; node_t *right = NULL,*left = NULL; while(n) { node_t *tmp = n; n = n->next; list_add_node_t(tmp->value > value ? &right : &left, tmp); } stack[top++] = left; stack[top++] = pivot; stack[top] = right; } *list = result; return; } ``` - 藉由宣告一個結構指標陣列,來實作 stack ,所以最大的深度為1000,若是超過程式會有錯誤。 - 每一次 quick sort subroutine 執行完後,會將 left, pivot, right 依照順序放入 stack 中,接下來從 stack 的頂端拿出一個進行排序,結束的時間點就是stack 為空時。其餘的操作就如同上述的遞迴方式。