# 2024q1 Homework2 (quiz1+2)
contributed by < `jychen0611` >
## [第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1)
### quiz 1
#### <程式運作原理>
結構體
```c
typedef struct __node {
struct __node *left, *right;
struct __node *next;
long value;
} node_t;
```
以下分別為取得串列尾節點和取得串列長度之函式。
因為 `left` 為節點指標的指標,`(*left)`表指向該節點的指標,依據判斷可知 AAAA 應填入`(*left)->next` ,而 `left = &((*left)->next)` 表`left` 指向隔壁節點指標的位址。
其函式回傳為指向串列尾端節點的指標。
同理,BBBB 應填入`(*left)->next` ,表`left` 指向隔壁節點指標的位址。
```c
node_t *list_tail(node_t **left)
{
while ((*left) && (*left)->next)
left = &(AAAA);
return *left;
}
int list_length(node_t **left)
{
int n = 0;
while (*left) {
++n;
left = &(BBBB);
}
return n;
}
```
#### <圖解說明>
```graphviz
digraph g1 {
node[shape=plaintext];
left [fontcolor="brown"];
p0 [fontcolor="black"];
p1 [fontcolor="black"];
p2 [fontcolor="black"];
p3 [fontcolor="black"];
p4 [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
p3 -> n3 -> p1 -> n1 -> p0 -> n0 -> p2 -> n2 -> p4 -> n4 -> l0;
}
left -> p3;
}
```
```graphviz
digraph g2 {
node[shape=plaintext];
left [fontcolor="brown"];
p0 [fontcolor="black"];
p1 [fontcolor="black"];
p2 [fontcolor="black"];
p3 [fontcolor="black"];
p4 [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
p3 -> n3 -> p1 -> n1 -> p0 -> n0 -> p2 -> n2 -> p4 -> n4 -> l0;
}
left -> p1;
}
```
以下是運用 Optimized QuickSort — C Implementation (Non-Recursive) 所實作非遞迴 (non-recursive; iterative) 的快速排序法程式碼。
* begin[i], end[i] 分別用來存放單一排序分割的起始點和終點,i 為分割編號
* left 用來存放小於等於 pivot 的節點
* right 用來存放大於 pivot 的節點
* result 用來存放排序完的分割
其流程為
* 以 L R 指向`串列分割 i` 之兩端,當 L R 尚未交會則進行內部排序
* 最左邊的數設為 pivot,p 則指向 pivot 隔壁的節點,pivot 的 next 指向 NULL
* 指標 p 會一直往右走,將大於 pivot 的節點存入 right , 小於的存入 left
* 由上述可知 CCCC 應填入 `p -> next`
* 接著可得到三組分割 left pivot right,其對應的分割編號分別為 i i+1 i+2
* 由上述可知 DDDD 應填入 `list_tail(left)` , EEEE 應填入 `list_tail(right)`
* 下一回合將進行 `串列分割 i+2` 之內部排序(也就是先排右邊)
* 直到右邊都排序完成後,才會將排序完的分割存入 result,並開始進行左邊分割的排序。
```c
void quick_sort(node_t **list)
{
int n = list_length(list);
int value;
int i = 0;
int max_level = 2 * n;
node_t *begin[max_level], *end[max_level];
node_t *result = NULL, *left = NULL, *right = NULL;
begin[0] = *list;
end[0] = list_tail(list);
while (i >= 0) {
node_t *L = begin[i], *R = end[i];
if (L != R) {
node_t *pivot = L;
value = pivot->value;
node_t *p = pivot->next;
pivot->next = NULL;
while (p) {
node_t *n = p;
p = CCCC;
list_add(n->value > value ? &right : &left, n);
}
begin[i] = left;
end[i] = DDDD;
begin[i + 1] = pivot;
end[i + 1] = pivot;
begin[i + 2] = right;
end[i + 2] = EEEE;
left = right = NULL;
i += 2;
} else {
if (L)
list_add(&result, L);
i--;
}
}
*list = result;
}
```
#### <圖解說明>
```graphviz
digraph g1 {
node[shape=plaintext];
"begin[0]" [fontcolor="brown"];
"end[0]" [fontcolor="brown"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
p [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
n3 -> n1 -> n0 -> n2 -> n4 -> l0
}
"begin[0]" -> n3;
"end[0]" -> n4;
pivot -> n3;
L -> n3;
R -> n4;
p -> n1;
}
```
```graphviz
digraph g2 {
node[shape=plaintext];
"left" [fontcolor="purple"];
"right" [fontcolor="purple"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
p [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
l1 [label ="NULL", fontcolor="black"];
l2 [label ="NULL", fontcolor="black"];
l3 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
n1 -> n0 -> n2 -> n4 -> l0
}
pivot -> n3 -> l1;
left -> l2;
right -> l3;
L -> n3;
R -> n4;
p -> n1;
}
```
```graphviz
digraph g3 {
node[shape=plaintext];
"left" [fontcolor="purple"];
"right" [fontcolor="purple"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
p [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
l1 [label ="NULL", fontcolor="black"];
l2 [label ="NULL", fontcolor="black"];
l3 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
n0 -> n2 -> n4 -> l0
}
pivot -> n3 -> l1;
left -> n1 -> l2;
right -> l3;
L -> n3;
R -> n4;
p -> n0;
}
```
```graphviz
digraph g4 {
node[shape=plaintext];
"left" [fontcolor="purple"];
"right" [fontcolor="purple"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
p [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
l1 [label ="NULL", fontcolor="black"];
l2 [label ="NULL", fontcolor="black"];
l3 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
n2 -> n4 -> l0
}
pivot -> n3 -> l1;
left -> n1 -> n0 -> l2;
right -> l3;
L -> n3;
R -> n4;
p -> n2;
}
```
```graphviz
digraph g5 {
node[shape=plaintext];
"left" [fontcolor="purple"];
"right" [fontcolor="purple"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
p [fontcolor="black"];
l0 [label ="NULL", fontcolor="black"];
l1 [label ="NULL", fontcolor="black"];
l2 [label ="NULL", fontcolor="black"];
l3 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
}
left -> n1 -> n0 -> n2 -> l0;
pivot -> n3 -> l1;
right -> l2;
L -> n3;
R -> n4;
p -> n4;
}
```
```graphviz
digraph g6 {
node[shape=plaintext];
"begin[0]" [fontcolor="brown"];
"end[0]" [fontcolor="brown"];
"begin[1]" [fontcolor="brown"];
"end[1]" [fontcolor="brown"];
"begin[2]" [fontcolor="brown"];
"end[2]" [fontcolor="brown"];
"left" [fontcolor="purple"];
"right" [fontcolor="purple"];
pivot [fontcolor="red"];
L [fontcolor="blue"];
R [fontcolor="blue"];
l1 [label ="NULL", fontcolor="black"];
l2 [label ="NULL", fontcolor="black"];
l3 [label ="NULL", fontcolor="black"];
node[shape=box];
n0 [label= "0"];
n1 [label= "1"];
n2 [label= "2"];
n3 [label= "3"];
n4 [label= "4"];
{
rank="same";
}
"begin[0]" -> n1;
"end[0]" -> n2;
"begin[1]" -> n3;
"end[1]" -> n3;
"begin[2]" -> n4;
"end[2]" -> n4;
pivot -> n3 -> l1;
left -> n1 -> n0 -> n2 -> l3;
right -> n4 -> l2;
L -> n3;
R -> n4;
}
```
### quiz 2
#### <程式運作原理>
以下為 Timsort merge 過程。
由於 tail 為指標的指標,AAAA 應填入 `&head` ,表指標 head 的位址。
同理, BBBB 應該填入 `&a->next` , CCCC 應該填入 `&b->next` 表指向下個節點指標的位址(更新 tail)。
```c
static struct list_head *merge(void *priv,
list_cmp_func_t cmp,
struct list_head *a,
struct list_head *b)
{
struct list_head *head;
struct list_head **tail = AAAA;
for (;;) {
/* if equal, take 'a' -- important for sort stability */
if (cmp(priv, a, b) <= 0) {
*tail = a;
tail = BBBB;
a = a->next;
if (!a) {
*tail = b;
break;
}
} else {
*tail = b;
tail = CCCC;
b = b->next;
if (!b) {
*tail = a;
break;
}
}
}
return head;
}
```
以下為補上雙向鏈結串列中的 `prev` 之函式,過程如下:
* 先將 tail 的 `next` 指向 head
* 以 `list` 逐步走訪整個串列,將 link 所指節點的 `prev` 指向 tail 。
* 最後再補上 tail 的 `next` 指向 head , head 的 `prev` 指向 tail 之動作
由上述可知 DDDD 為 `tail->next` ,EEEE 為 `head->prev`
```c
static void build_prev_link(struct list_head *head,
struct list_head *tail,
struct list_head *list)
{
tail->next = list;
do {
list->prev = tail;
tail = list;
list = list->next;
} while (list);
/* The final links to make a circular doubly-linked list */
DDDD = head;
EEEE = tail;
}
```
下面是 timsort 程式碼。其過程如下 :
```c
void timsort(void *priv, struct list_head *head, list_cmp_func_t cmp)
{
stk_size = 0;
struct list_head *list = head->next, *tp = NULL;
if (head == head->prev)
return;
/* Convert to a null-terminated singly-linked list. */
head->prev->next = NULL;
do {
/* Find next run */
struct pair result = find_run(priv, list, cmp);
result.head->prev = tp;
tp = result.head;
list = result.next;
stk_size++;
tp = merge_collapse(priv, cmp, tp);
} while (list);
/* End of input; merge together all the runs. */
tp = merge_force_collapse(priv, cmp, tp);
/* The final merge; rebuild prev links */
struct list_head *stk0 = tp, *stk1 = stk0->prev;
while (stk1 && stk1->prev)
stk0 = stk0->prev, stk1 = stk1->prev;
if (stk_size <= FFFF) {
build_prev_link(head, head, stk0);
return;
}
merge_final(priv, cmp, head, stk1, stk0);
}
```
## [第 2 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz2)
### quiz 1
#### <程式運作原理>
### quiz 2
#### <程式運作原理>
### quiz 3
#### <程式運作原理>
## 參考資料
* [作業說明](https://hackmd.io/@sysprog/BkmmEQCn6)
* [你所不知道的C語言:指標篇](https://hackmd.io/@sysprog/c-pointer)
* [你所不知道的 C 語言:記憶體管理、對齊及硬體特性](https://hackmd.io/@sysprog/c-memory)
* [基於 C 語言標準研究與系統程式安全議題](https://hackmd.io/@sysprog/c-std-security)
* [你所不知道的 C 語言: bitwise 操作](https://hackmd.io/@sysprog/c-bitwise)
* [Linux 核心的 hash table 實作](https://hackmd.io/@sysprog/linux-hashtable)
* [Graphviz](https://graphviz.org/)