# 2024q1 Homework2 (quiz1+2) contributed by <[`bclegend`](https://github.com/bclegend)> ## [第一週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1) ### 測驗一 [程式碼](https://github.com/bclegend/2024q1hw2/tree/main/qsort) #### 運作原理 實做 quick sort 演算法,並以以下 `node_t` 為 linked list 的基礎架構,排序 linked list 中數值的序列。 ```c typedef struct __node { struct __node *left, *right; struct __node *next; long value; } node_t; ``` * 原始 linked list 由下圖所示,在開始時將陣列的最左方的元素紀錄為 `pivot` ```graphviz digraph structs { node[shape=plaintext]; pivot [fontcolor="red"]; L [fontcolor="blue"]; R [fontcolor="blue"]; begin node[shape=box]; struct0 [label= "4"]; struct1 [label= "5"]; struct2 [label= "3"]; struct3 [label= "2"]; struct4 [label= "1"]; struct5 [label= "7"]; struct6 [label= "9"]; struct7 [label= "8"]; { rank="same"; struct0 -> struct1 struct1 -> struct2 struct2 -> struct3 struct3 -> struct4 struct4 -> struct5 struct5 -> struct6 struct6 -> struct7 } pivot -> struct0 L -> struct0 R -> struct7 } ``` * 首先,移動指標 `R` 往 linked list 左方移動,若 `R` 指向的位置儲存的數值小於 `pivot` ,將指標 `R`指向位置的數值與指標 `L` 指向位置的數值交換 ```graphviz digraph structs { node[shape=plaintext]; pivot [fontcolor="red"]; L [fontcolor="blue"]; R [fontcolor="blue"]; node[shape=box]; structp [label="4"] struct0 [label= "1"]; struct1 [label= "5"]; struct2 [label= "3"]; struct3 [label= "2"]; struct4 [label= "4"]; struct5 [label= "7"]; struct6 [label= "9"]; struct7 [label= "8"]; { rank="same"; struct0 -> struct1 struct1 -> struct2 struct2 -> struct3 struct3 -> struct4 struct4 -> struct5 struct5 -> struct6 struct6 -> struct7 } pivot -> structp L -> struct0 R -> struct4 } ``` * 再來移動指標 `L` 的位置往右方移動,若 `L` 指向的位置儲存的數值大於 `pivot` ,將指標 `L`指向位置的數值與指標 `R` 指向位置的數值交換 ```graphviz digraph structs { node[shape=plaintext]; pivot [fontcolor="red"]; L [fontcolor="blue"]; R [fontcolor="blue"]; node[shape=box]; structp [label="4"] struct0 [label= "1"]; struct1 [label= "4"]; struct2 [label= "3"]; struct3 [label= "2"]; struct4 [label= "5"]; struct5 [label= "7"]; struct6 [label= "9"]; struct7 [label= "8"]; { rank="same"; struct0 -> struct1 struct1 -> struct2 struct2 -> struct3 struct3 -> struct4 struct4 -> struct5 struct5 -> struct6 struct6 -> struct7 } pivot -> structp L -> struct1 R -> struct4 } ``` ```graphviz digraph structs { node[shape=plaintext]; pivot [fontcolor="red"]; L [fontcolor="blue"]; R [fontcolor="blue"]; node[shape=box]; structp [label="4"] struct0 [label= "1"]; struct1 [label= "2"]; struct2 [label= "3"]; struct3 [label= "4"]; struct4 [label= "5"]; struct5 [label= "7"]; struct6 [label= "9"]; struct7 [label= "8"]; { rank="same"; struct0 -> struct1 struct1 -> struct2 struct2 -> struct3 struct3 -> struct4 struct4 -> struct5 struct5 -> struct6 struct6 -> struct7 } pivot -> structp L -> struct1 R -> struct3 } ``` * 重複以上動作直到 `L` 與 `R` 指向同一個位置 ```graphviz digraph structs { node[shape=plaintext]; pivot [fontcolor="red"]; L [fontcolor="blue"]; R [fontcolor="blue"]; linkedlist_0 [fontcolor = "black"] linkedlist_3 [fontcolor = "black"] linkedlist_7 [fontcolor = "black"] node[shape=box]; structp [label="4"] struct0 [label= "1"]; struct1 [label= "2"]; struct2 [label= "3"]; struct3 [label= "4",fontcolor = "red"]; struct4 [label= "5"]; struct5 [label= "7"]; struct6 [label= "9"]; struct7 [label= "8"]; { rank="same"; struct0 -> struct1 struct1 -> struct2 struct2 -> struct3 struct3 -> struct4 struct4 -> struct5 struct5 -> struct6 struct6 -> struct7 } pivot -> structp L -> struct3 R -> struct3 linkedlist_0 -> struct0 linkedlist_3 -> struct3 linkedlist_7 -> struct7 } ``` 此時 linkedlist[0] 到 linkedlist[2] 為新的 linked list 1 ,linkedlist[3]為新的 linked list 2 , linkedlist[4] 到 linkedlist[7] 為新的 linked list 3 並將這三個新的 linked list 再繼續排列直到整個 linked list 排列完成 而在我們的實做中,我們利用 `struct __node *left, *right;` 進行堆疊,堆疊出左方的 linked list `left` 以及右方的 linked list `right` 這次的實做中,我們使用的是改變 `node` 中數值的方式 #### 改進 1. 是否需要在配置 linked list `node_t *begin[max_level], *end[max_level];` 時給 `max_level` 這麼多的空間? #### 使用 Linux 核心風格的 List API 改寫上述程式碼 ### 測驗二 ## [第二週測驗題](https://hackmd.io/@sysprog/linux2024-quiz2) ### 測驗一 [Leetcode](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/) 在二元樹的構建過程中,我們可以利用不同的排序方式來唯一確定一顆樹的結構。其中,`preorder` 採用的是"中->左->右"的順序,`inorder` 採用的是"左->中->右"的順序。如果我們知道這兩種排序方式,就足以建構出唯一的二元樹。 程式的實作方式是利用 Linux 核心的 hash table。在這個實作中,我們首先以preorder的順序找到根節點的值,接著在inorder中找到對應的左子樹和右子樹元素。然後,我們將左右子樹當作新的根節點,再次進行相同的過程,直到找不到對應的元素為止。 ```c static struct TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) { struct hlist_head *in_heads = malloc(inorderSize * sizeof(*in_heads)); for (int i = 0; i < inorderSize; i++) INIT_HLIST_HEAD(&in_heads[i]); for (int i = 0; i < inorderSize; i++) node_add(inorder[i], i, inorderSize, in_heads); return dfs(preorder, 0, preorderSize - 1, inorder, 0, inorderSize - 1, in_heads, inorderSize); } ``` 在 linux kernel 中 [`include/linux/types.h`](https://github.com/torvalds/linux/blob/master/include/linux/types.h) 定義 `hlist_node` 以及 `hlist_head` 結構如下 ```c struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; ``` ```graphviz digraph G { rankdir = LR; splines = false; node[shape = "record"] list_head[label = "<m>list_head | <n>first"] node_1[label = "<m>hlist_node_1 | {<p>pprev | <n>next}", group = list]; node_2[label = "<m>hlist_node_2 | {<p>pprev | <n>next}", group = list]; node_3[label = "<m>hlist_node_3 | {<p>pprev | <n>next}", group = list]; NULL[shape = plaintext, label = "NULL", group = list] {rank = "min" list_head} list_head -> node_1 -> node_2 -> node_3[ weight = 10, style = invis ] list_head:n -> node_1:m; node_1:p -> list_head:n; node_1:n -> node_2:m; node_2:p -> node_1:n; node_2:n -> node_3:m; node_3:p -> node_2:n; node_3:n -> NULL; } ``` * `hlist_add_head` 在 hash table 中的 `head` 後新增節點 ```diff static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { if (h->first) h->first->pprev = &n->next; - n->next = AAAA; + n->next = h->first; n->pprev = &h->first; h->first = n; } ``` * `find` ```diff static int find(int num, int size, const struct hlist_head *heads) { struct hlist_node *p; int hash = (num < 0 ? -num : num) % size; - hlist_for_each (p, BBBB) { - struct order_node *on = CCCC(p, struct order_node, node); + hlist_for_each (p, &heads[hash]) { + struct order_node *on = list_entry(p, struct order_node, node); if (num == on->val) return on->idx; } return -1; } ``` * `node_add` 在 hash table 中新增節點 ```diff static inline void node_add(int val, int idx, int size, struct hlist_head *heads) { struct order_node *on = malloc(sizeof(*on)); on->val = val; on->idx = idx; int hash = (val < 0 ? -val : val) % size; - hlist_add_head(&on->node, DDDD); + hlist_add_head(&on->node, &heads[hash]); } ``` ### 測驗二 [Leetcode](https://leetcode.com/problems/lru-cache/description/) ### 測驗三