# 2021q1 Homework1 (quiz1) contributed by < `Tzuying` > ###### tags: `linux2021` > [J02: quiz1](https://hackmd.io/@sysprog/SJlXFVMzMu) :::success * [x] 圖形化 [Graphviz](https://graphviz.org/) * [ ] Real [Random](https://linux.die.net/man/3/random), [Pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) * [ ] 避免使用遞迴呼叫 [Optimized QuickSort — C Implementation (Non-Recursive)](https://alienryderflex.com/quicksort/) * [ ] 探討 Linux 的 linked list 和上述程式碼的落差,並改寫 [linux-list](https://github.com/sysprog21/linux-list) 的 quick sort 範例,避免使用遞迴呼叫 * [ ] 思考高效率的 linked list 排序演算法,並落實於上述 (3) 的程式碼中 * [A Comparative Study of Linked List Sorting Algorithms](https://pages.mtu.edu/~shene/PUBLICATIONS/1996/3Conline.pdf) by [冼鏡光](https://zh.wikipedia.org/zh-tw/%E5%86%BC%E9%8F%A1%E5%85%89) ::: ## Preview * `node_t *node_t` 與 `node_t **list` * 指標 = 值為記憶體的變數 * 設計變數為記憶體,表示未來希望改變的是該記憶體存放的數值(需要*) * 當函式參數為指標,可能是一個 node,也可能是一串 node 的開頭 (array or llist),需透過上下文確認 * 記憶體變數的最終目是(使用)改變記憶體存放的數值,為了達到這個目的,需要各種記憶體變數操作 * 製作記憶體不連續的多元素容器結構, linked list * 作為瀏覽多元素容器結構的 index * 為函式加註解以理解各自作用 [GitHub(wait)]() * [Boolean type support library](https://en.cppreference.com/w/c/types/boolean), from C99 * [Quicksort](https://en.wikipedia.org/wiki/Quicksort) * The shaded element is the ==pivot==. It is always chosen as the last element of the partition. ![](https://i.imgur.com/KCwPRka.png) ## 解題 * ANS(Try-1) : d / e / c / b * implment `list_make_node_t` and `list_free` * run `gcc -o quiz ./main.c ./llist.c; ./quiz` * result = null * ![](https://i.imgur.com/HKkypi3.png) * 思路 * list 有建置成功,所以問題不會在 AAA 或 BBB,而 CCC 其他答案都不可能,所以錯誤應該是 LLL * ANS(Try-2) : **c** / e / c / b * ==盲點== `list_concat` * 函式目的很清楚,讓 left 走到尾端,將 right 放在其 next * 觀察 caller `quicksort`,result 要一直放置 list 的開頭,所以 list_concat 中走 Linked List 的動作不能在 `node_t *` 層級,要在 list 結構的記憶體層級 `node_t **`,否則最後 result 都會指在 list 結尾 NULL ```cpp list_concat(&result, left); list_concat(&result, pivot); list_concat(&result, right); *list = result; ``` * 換句話說,不能透過 next `result = result->next` 移動,要透過 next 的記憶體位置 `&result = &(result->next)` 移動 * 回到選項,以圖片表示兩種作法差異,假設 list => 1016, 84, 706 * 執行前 ```graphviz digraph before { rankdir=LR subgraph cluster_0 { style=filled; color=lightgrey; node1 [shape=record, label="{ <value>1016 | <ref> A(node2) }",color=darkgreen] node2 [shape=record, label="{ <value>84 | <ref> A(node3) }",color=darkgreen] node3 [shape=record, label="{ <value>706 | <next>NULL }",color=darkgreen]; node1 -> node2 -> node3 ; label = "the list"; } subgraph cluster_1 { color=white; left [shape=box, label="A(result)", width=1.5]; label = "left"; } subgraph cluster_2 { color=white; result[shape=box, label="A(node1)"]; label = "result"; } left -> result; result -> node1:value ; } ``` * `d` `*left = (*left)->next` ```graphviz digraph before { rankdir=LR subgraph cluster_0 { style=filled; color=lightgrey; node1 [shape=record, label="{ <value>1016 | <next> A(node2) }",color=darkgreen] node2 [shape=record, label="{ <value>84 | <next> A(node3) }",color=darkgreen] node3 [shape=record, label="{ <value>706 | <next>NULL }",color=darkgreen]; node1 -> node2 -> node3 ; label = "the list"; } subgraph cluster_1 { color=white; left [shape=box, label="A(result)", width=1.2]; label = "left"; } subgraph cluster_2 { color=white; result[shape=box, label="A(node2)", width=1.2]; label = "*left(result)"; } left -> result; result -> node2:value ; } ``` * `c` `left = &((*left)->next)` ```graphviz digraph before { rankdir=LR subgraph cluster_0 { style=filled; color=lightgrey; node1 [shape=record, label="{ <value>1016 | <next> A(node2) }",color=darkgreen] node2 [shape=record, label="{ <value>84 | <next> A(node3) }",color=darkgreen] node3 [shape=record, label="{ <value>706 | <next>NULL }",color=darkgreen]; node1 -> node2 -> node3 ; label = "the list"; } subgraph cluster_1 { color=white; left [shape=box, label="A(node1->next))", width=1.7]; label = "left"; } subgraph cluster_3 { color=white; pleft[shape=box, label="A(node2)", width=1.2]; label = "*left"; } subgraph cluster_2 { color=white; result[shape=box, label="A(node1)", width=1.2]; label = "result"; } left -> pleft -> node2:value ; } ``` ```graphviz digraph before { rankdir=LR subgraph cluster_0 { style=filled; color=lightgrey; node1 [shape=record, label="{ <value>1016 | <next> A(node2) }",color=darkgreen] node2 [shape=record, label="{ <value>84 | <next> A(node3) }",color=darkgreen] node3 [shape=record, label="{ <value>706 | <next>NULL }",color=darkgreen]; node1 -> node2 -> node3 ; label = "the list"; } subgraph cluster_1 { color=white; left [shape=box, label="A(node2->next))", width=1.7]; label = "left"; } subgraph cluster_3 { color=white; pleft[shape=box, label="A(node3)", width=1.2]; label = "*left"; } subgraph cluster_2 { color=white; result[shape=box, label="A(node1)", width=1.2]; label = "result"; } left -> pleft -> node3:value ; } ``` * ==比較== `void list_add_node_t(node_t **list, node_t *node_t)` vs `node_t * list_make_node_t(node_t *list, long value)` * 這兩個函式都是要建立一個 list ,為什麼輸入參數型態可以不同 * `void list_add_node_t(node_t **list, node_t *node_t)` * 將 `node_t **list` 改為 `node_t *list`,發現 list 只剩下 pivot * 其實是簡單的傳值傳址觀念,若有 list 結構改變,需要傳址(&(node_t * list))進函式,而只有透過 `*` 操作才會實際有變化 * 假設某函式輸入參數如下,在該函式裡對 a 做任意給值都不會影響函式外部。反過來說,若 a 的層級越上,能做的真實改變範圍就越大 * `node_t a` * `node_t *a` => 對 `*a` 的改變會真的生效 * `node_t **a` => 對 `*a` / `**a` 的改變會真的生效 * `node_t * list_make_node_t(node_t *list, long value)` * 雖然改變儘限於函式中,但因為有以回傳值接起來,所以也沒有問題 ## Graphviz * 可以在 hackmd 上直接寫 Graphviz 程式碼,就會自動產生圖片 :sparkles: * [documentation](https://graphviz.org/documentation/) * [node shape](https://graphviz.org/doc/info/shapes.html) * [attribute](https://graphviz.org/doc/info/attrs.html) * [Gallery](https://graphviz.org/gallery/) : [Hello World](https://graphviz.org/Gallery/directed/hello.html) ```graphviz digraph G { Hello->World } ``` * [如何編譯與程式語言簡介](http://wiki.csie.ncku.edu.tw/Graphviz?revision=7e4c6cf2373e41d948c8fb5caffd0407b9f302d7) * 參考範例 [2018q1 Homework (quiz4)](https://hackmd.io/2iFLLGRyT2SzIAV-Hwf1NA?both) ```graphviz digraph R { rankdir=LR subgraph cluster_0 { style=filled; color=lightgrey; node1 [shape=record, label="{ <value>1016 | <ref> }",color=darkgreen] node2 [shape=record, label="{ <value>84 | <ref> }",color=darkgreen] node3 [shape=record, label="{ <value>706 | <next>NULL }",color=darkgreen]; node1:ref:c -> node2 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; node2:ref:c -> node3 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; label = "the list"; } subgraph cluster_1 { color=white; node[shape=box, label=""]; left; label = "left"; } subgraph cluster_2 { color=white; node[shape=box, label=""]; result; label = "result"; } left -> result [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; result -> node1:value [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` ## 重點整理 * 變動結構的函式其 list/head 參數 * 需傳址 (除非有在 caller 做特殊處理) * 需考慮離開函式之後,指標參數的值在哪裡,是否符合設計