# 2021q1 Homework1 (quiz1) contributed by < `hapion` > ## :memo: 預期目標 * 檢驗學員對於 C 語言指標操作的熟悉程度 * 檢驗學員對於遞迴呼叫的認知 * 搭配 [Graphviz](https://graphviz.org/) 在 HackMD 筆記上視覺化展現 ## 程式運作原理 - 將 node 添加至 list 的最前端 ```c static inline void list_add_node_t(node_t **list, node_t *node_t) { node_t->next = *list; *list = node_t; } ``` ```graphviz digraph graphname{ node[shape=box] "*list"[shape=plaintext,fontcolor=red] rankdir=LR { rankdir = LR node_t A[label=A] B[label=B] C[label=C] NULL1[label=NULL,shape=plaintext] } { rank="same"; "*list"->A } node_t->A A->B->C->NULL1 } ``` ```graphviz digraph graphname{ node[shape=box] "*list"[shape=plaintext,fontcolor=red] rankdir=LR { rankdir = LR node_t A[label=A] B[label=B] C[label=C] NULL1[label=NULL,shape=plaintext] } { rank="same"; "*list"->node_t } node_t->A A->B->C->NULL1 } ``` - 將兩個 list 串連起來 ``` c= static inline void list_concat(node_t **left, node_t *right) { while (*left) left = &((*left)->next); *left = right; } ``` ``` c static inline void list_concat(node_t **left, node_t *right) ``` ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structp [label="left|<p>&(*left)"] structptr [label="<name_ptr> *left|<ptr> &A"]; structb [label="<B> B|2"]; structa [label="<A> A|1"]; structc [label="<C> C|3"]; structptr:ptr -> structa:A:nw structp:p -> structptr:ptr:nw } ``` ``` graphviz digraph structs { node[shape=record] {rank=same; structd,structe,structf} structptr [label="<name_ptr> *right|<ptr> &D"]; structe [label="<E> E|2"]; structd [label="<D> D|1"]; structf [label="<F> F|3"]; structptr:ptr -> structd:D:nw } ``` ```c #2 while (*left) #3 left = &((*left)->next) ; ``` ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structp [label="left|<p>&(*left)"] structptr [label="<name_ptr> *left|<ptr> &A"]; structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; structptr:ptr -> structb:B:nw structp:p -> structptr:ptr:nw } ``` ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structp [label="left|<p>&(*left)"] structptr [label="<name_ptr> *left|<ptr> &A"]; structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; structptr:ptr -> structc:C:nw structp:p -> structptr:ptr:nw } ``` ```c #4 *left = right; ``` ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc,structd,structe,structf} structp [label="left|<p>&(*left)"] structptr [label="<name_ptr> *left|<ptr> &A"]; structrptr [label="<name_ptr> *right|<ptr> &D"]; structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; structe [label="<E> E|R2"]; structd [label="<D> D|R1"]; structf [label="<F> F|R3"]; structptr:ptr -> structd:D:nw structp:p -> structptr:ptr:nw structrptr:ptr -> structd:D:nw } ``` - quicksort ``` c= 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; } ``` ```c #6 node_t *pivot = *list; #7 int value = pivot->value; ``` 宣告一指標變數 pivot 指向 ```*list``` 所指的 node 宣告一整數變數 value 存 pivot 所指 node 內的值 ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structp [label="list|<p>&(*list)"] structptr [label="<name_ptr> *list|<ptr> &A"]; structpivot [label="<name_pivot> *pivot|&A"]; structvalue [label="<name_value> value|L1"]; structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; NULL1[label=NULL,shape=plaintext] structpivot -> structa structvalue -> NULL1 structptr:ptr -> structa:A:nw structp:p -> structptr:ptr:nw } ``` ``` c #8 node_t *p = pivot->next; ``` 宣告一指標變數 p 指向 ```pivot->next``` ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structl [label="list|<p>&(*list)"] structptr [label="<name_ptr> *list|<ptr> &A"]; structpivot [label="<name_pivot> *pivot|&A"]; structvalue [label="<name_value> value|L1"]; structp [label="<name_p> *p|&B"] structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; NULL1[label=NULL,shape=plaintext] structp -> structb structpivot -> structa structvalue -> NULL1 structptr:ptr -> structa:A:nw structl:l -> structptr:ptr:nw } ``` ``` c #9 pivot->next = NULL; ``` ```A->next``` 原本指向 B,將其設為 NULL ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structvalue [label="<name_value> value|L1"]; structl [label="list|<p>&(*list)"] structptr [label="<name_ptr> *list|<ptr> &A"]; structpivot [label="<name_pivot> *pivot|&A"]; structp [label="<name_p> *p|&B"] structb [label="<B> B|L2"]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] structp -> structb structpivot -> structa structvalue -> NULL1 structptr:ptr -> structa:A:nw structl:l -> structptr:ptr:nw structa -> NULL2 } ``` ```c 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); } ``` >宣告 ```*left``` 、 ```*right``` 兩個 pointer 指向 NULL ``` graphviz digraph structs { node[shape=record] left [label="*left|"]; right [label="*right|"]; NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] left -> NULL1 right -> NULL2 } ``` >宣告 ```*n``` 指向原本 ```*p``` 所指的 node ,也就是 B ``` graphviz digraph structs { node[shape=record] {rank=same; structa,structb,structc} structvalue [label="<name_value> value|L1"]; structl [label="list|<p>&(*list)"] structptr [label="<name_ptr> *list|<ptr> &A"]; structpivot [label="<name_pivot> *pivot|&A"]; structn [label="<name_n> *n|&B", color=red] structp [label="<name_p> *p|&B"] structb [label="<B> B|L2",color=red]; structa [label="<A> A|L1"]; structc [label="<C> C|L3"]; NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] structn -> structb structp -> structc structpivot -> structa structvalue -> NULL1 structptr:ptr -> structa:A:nw structl:l -> structptr:ptr:nw structa -> NULL2 } ``` >比較 ```n->value``` 與 ```value``` ( L2 && L1 ) 若大於,則將 n 添加至 right ``` graphviz digraph structs { node[shape=record] structb [label="<B> B|L2",color=red]; left [label="*left|"]; right [label="*right|&B"]; NULL1[label=NULL,shape=plaintext] left -> NULL1 right -> structb } ``` > 若小於,則將 n 添加至 left ``` graphviz digraph structs { node[shape=record] structb [label="<B> B|L2",color=red]; left [label="*left|"]; right [label="*right|&B"]; NULL1[label=NULL,shape=plaintext] left -> structb right -> NULL1 } ``` ``` c quicksort(&left); quicksort(&right); node_t *result = NULL; list_concat(&result, left); list_concat(&result, pivot); list_concat(&result, right); *list = result; ``` >分別對 left list,right list 遞迴執行 quicksort 執行完後會有兩個排序好的串列 然後宣告一個 ```*result``` 將 left right pivot 透過 ```list_concat``` 串連起來 :::warning :information_source: 要記得 ```*pivot``` 還沒被加進去 ::: ## 修正 random 並引入其他 Pseudorandom number generator - 原本測試的程式中,是直接使用 random() 來取得亂數,會發現數字一樣,所以使用 srand 來取用 ``` c= int main(int argc, char **argv) { size_t count = 20; srand48(time(NULL)); node_t *list = NULL; while (count--) list = list_make_node_t(list, lrand48() % 1024); list_display(list); quicksort(&list); list_display(list); if (!list_is_ordered(list)) return EXIT_FAILURE; list_free(&list); return EXIT_SUCCESS; } ``` ## 參考 [Optimized QuickSort — C Implementation (Non-Recursive)](https://alienryderflex.com/quicksort/) 並重寫上述 quick sort 程式碼,避免使用遞迴呼叫