# 2021q1 Homework (quiz1)
###### tags: `linux2021` ,`linked list`
contributed by < [cymb103u](https://github.com/cymb103u) >
* [第1週測驗題目](https://hackmd.io/@sysprog/linux2021-quiz1)
----
- [x] 1.解釋程式原理
- [ ] 2.引入其他 Pseudorandom number generator
- [x] 3.非遞迴呼叫改寫
- [ ] 4.[仿效 linux-list 並改寫](#延伸問題)
- [ ] 5.思考高效率的 linked list 排序演算法
- [ ] [rbtree](#Tree-sort)
----
## 1. 解釋程式運行原理
* 給定之 data structure,為單向 linked list,typedef 為 `node_t`
```cpp
typedef struct __node {
int value;
struct __node *next;
} node_t;
```
> * 題目預期 : 已知不存在 circular (環狀結構),下方程式碼嘗試對該單向 linked list 進行 Quick sort,預期依據遞增順序。
* [static inline 舉例說明](https://medium.com/@hauyang/%E6%88%91%E6%9C%89%E6%89%80%E4%B8%8D%E7%9F%A5%E7%9A%84-static-inline-b363892b7450)
```cpp=
static inline void list_add_node_t(node_t **list, node_t *node_t) {
node_t->next = *list;
*list = node_t;
}
```
* `list_add_node_t` 函式是將 `node_t` 節點加入到 `*list` 之前,並將 `*list` 原本的值更新為 `node_t` 指向整個串列的最前端。
*
```diff=5
static inline void list_concat(node_t **left, node_t *right) {
while (*left)
- LLL;
+ left = &((*left)->next);
*left = right;
}
```
### 1. LLL = `left = &((*left)->next)`
`list_concat()` function 的功用為串接兩個 list。根據給定的 implement 推測它是要走訪 left list 到 end(NULL),再將左右 list 接在一起。而給定的 input `left` 為 `pointer to pointer to node_t`; `right` 則是 `pointer to node_t`。
```graphviz
digraph graphname{
node[shape=box]
"**left"[shape=plaintext,fontcolor=blue]
right[shape=plaintext]
rankdir=LR
{
rankdir = LR
node1[label="(*left) node1"]
node2[label=node2]
nodeN[label=nodeN]
rnode[label=rnode]
NULL1[label=NULL,shape=plaintext]
NULL2[label=NULL,shape=plaintext]
}
{
rank="same";
"**left"->node1
}
node1->node2->nodeN->NULL1
right->rnode->NULL2
}
```
一開始以為是 `*left = (*left)->next`,但這樣子實際上會改變 `left` 所指向的 address 裡面的值 ( pointer to node_t ),而非改變 `left` 所指向的 address
```graphviz
digraph graphname{
node[shape=box]
"**left"[shape=plaintext,fontcolor=blue]
right[shape=plaintext]
rankdir=LR
{
rankdir = LR
node2[label="(*left) node2"]
nodeN[label=nodeN]
rnode[label=rnode]
NULL1[label=NULL,shape=plaintext]
NULL2[label=NULL,shape=plaintext]
}
{
rank="same";
"**left"->node2
}
node2->nodeN->NULL1
right->rnode->NULL2
}
```
所以應該使用 `left = &((*left)->next)` 來更新 `left` 所指向的 address
* 1
```graphviz
digraph graphname{
node[shape=box]
"**left"[shape=plaintext,fontcolor=blue]
right[shape=plaintext]
rankdir=LR
{
rankdir = LR
node1[label=node1]
node2[label="(*left) node2"]
nodeN[label=nodeN]
rnode[label=rnode]
NULL1[label=NULL,shape=plaintext]
NULL2[label=NULL,shape=plaintext]
}
{
rank="same";
"**left"->node2
}
node1->node2->nodeN->NULL1
right->rnode->NULL2
}
```
* 2
```graphviz
digraph graphname{
node[shape=box]
"**left"[shape=plaintext,fontcolor=blue]
right[shape=plaintext]
rankdir=LR
{
rankdir = LR
node1[label=node1]
node2[label=node2]
nodeN[label=nodeN]
rnode[label=rnode]
NULL1[label="(*left) NULL",shape=plaintext]
NULL2[label=NULL,shape=plaintext]
}
{
rank="same";
"**left"->NULL1
}
node1->node2->nodeN->NULL1
right->rnode->NULL2
}
```
* 3
```graphviz
digraph graphname{
node[shape=box]
"**left"[shape=plaintext,fontcolor=blue]
rankdir=LR
{
rankdir = LR
node1[label=node1]
node2[label=node2]
nodeN[label=nodeN]
rnode[label="(*left)rnode"]
NULL1[label=NULL,shape=plaintext]
}
{
rank="same";
"**left"->rnode
}
node1->node2->nodeN->rnode->NULL1
}
```
:::info
Reference :
* [你所不知道的C語言: linked list 和非連續記憶體操作](https://hackmd.io/@sysprog/c-linked-list)
:::
```diff=11
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 ? AAA : BBB, n);
+ list_add_node_t(n->value > value ? &right : &left, n);
}
quicksort(&left);
quicksort(&right);
node_t *result = NULL;
list_concat(&result, left);
- CCC;
+ list_concat(&result, pivot); list_concat(&result, right);
*list = result;
}
```
### 2. AAA =`&right`, BBB = `&left`
* 在這個 quicksort implement 裡,是以 `recursion`來進行。 所以用 `!*list` 來檢查是否要將這個函式中止。
* 而在 16~19 行看到將串列的開頭當做 `pivot` 取出後並將其 next 指向 NULL 來使它獨立
* 在找到一個 pivot 後比它大的放到`right`, 比它小的則是放到 `left`,所以 AAA 填入 `&right` BBB 填入 `&left`
* 並以遞迴的方式來繼續排序 `left` 和 `right`
### 3. CCC = `list_concat(&result, pivot); list_concat(&result, right);`
在最後將分開的 list 以 `list_concat()` 做串接,並將`*list` 指向為 result 。
----
## 2.引入其他 [Pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)
* 延伸:透過 `time(NULL)` 引入時間參數,來產生 random seed.
* [初探 Linux kernel 亂數產生器 – random generator](https://reurl.cc/undefined)
* [linD026](https://hackmd.io/@linD026/2021q1_quiz1#random-%E5%95%8F%E9%A1%8C)
----
## 3.非遞迴呼叫改寫
:::info
Reference :
1. [Optimized QuickSort — C Implementation (Non-Recursive)
](https://alienryderflex.com/quicksort/)
2. [Iterative Quick Sort](https://www.geeksforgeeks.org/iterative-quick-sort/)
:::
在原本的程式碼使用 recursive function 時,會需要 [function call stack ](https://en.wikipedia.org/wiki/Call_stack) 來紀錄變數、return address 等資訊,並且造成 overhead 減慢速度,所以採用 iterative 來做改寫。
> asd
>
```cpp=
void quicksort_iter(node_t **list)
{
if (!*list)
return;
node_t *stack[MAX_LEVELS];
int top = -1;
stack[++top] = *list;
node_t *partition = NULL;
node_t *result = NULL;
while (top >= 0) {
partition = stack[top--];
if (partition && partition->next != NULL) {
node_t *pivot = partition;
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);
}
// pivot handling
list_concat(&left, pivot);
// left and right
if (right)
stack[++top] = right;
if (left)
stack[++top] = left;
} else {
/* single pviot*/
top++;
while (top >= 0 && stack[top]->next == NULL) {
node_t *tmp = stack[top--];
list_concat(&result, tmp);
}
}
}
*list = result;
}
```
----
## 延伸問題
2. 參考 [Optimized QuickSort — C Implementation (Non-Recursive)](https://alienryderflex.com/quicksort/) 並重寫上述 quick sort 程式碼,避免使用遞迴呼叫
3. Linux 核心內部也有 linked list 實作,但是 circular doubly-linked list,[linux-list](https://github.com/sysprog21/linux-list) 仿效 Linux 核心的實作並予以簡化,在 `examples/` 目錄提供 quick sort 實作,請探討 Linux 的 linked list 和上述程式碼的落差,並改寫 linux-list 的 quick sort 範例,避免使用遞迴呼叫
* 參考資料: [The Linux Kernel API - List Management Functions](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html)
4. 研讀 Ching-Kuang Shene ([冼鏡光](https://zh.wikipedia.org/zh-tw/%E5%86%BC%E9%8F%A1%E5%85%89)) 教授撰寫的 [A Comparative Study of Linked List Sorting Algorithms](https://pages.mtu.edu/~shene/PUBLICATIONS/1996/3Conline.pdf),思考高效率的 linked list 排序演算法,並落實於上述 (3) 的程式碼中
## test
* cppcheck