# 2021q1 Homework1 (quiz1)
contributed by < `Shanola` >
[第一周題目](https://hackmd.io/@sysprog/linux2021-quiz1)
[GitHub程式碼](https://github.com/Shanola/NCKU-Linux2021-Quiz1)
## 測驗 `1`
### 程式碼
考慮**單向** linked list 結構,已知**無 circular**,嘗試以**遞增**順序進行 Quick sort,以下為有進行新增或修改的程式碼片段
```cpp=
static inline void list_concat(node_t **left, node_t *right) {
while (*left)
left = &((*left)->next);// LLL
*left = right;
}
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/*AAA*/ : &left/*BBB*/, n);
}
quicksort(&left);
quicksort(&right);
node_t *result = NULL;
list_concat(&result, left);
list_concat(&result, pivot);
list_concat(&result, right); ///CCC
*list = result;
}
node_t *list_make_node_t(node_t *list, int value)
{
node_t *n = malloc(1 * sizeof(node_t));
if (!n) {
assert(n && "malloc error");
return NULL;
}
n->value = value;
n->next = NULL;
n->prev = NULL;
if (!list) {
return n;
} else {
node_t *tmp = list;
while (tmp->next) {
tmp = (tmp->next);
}
tmp->next = n;
n->prev = tmp;
return list;
}
}
void list_free(node_t **list)
{
if (!(*list)->next) {
free(*list);
} else {
list_free(&((*list)->next));
}
}
```
### 程式碼解析
首先 `list_add_node_t` 是一個沒有回傳值 (void) 且在編譯時被展開到呼叫位置 (static inline) 的函式,具有一個指向 node_t 指標的指標 `list`,及指向 node_t 的指標 `node` 兩個參數,負責**將節點加到 list 前方**:
```cpp=
static inline void list_add_node_t(node_t **list, node_t *node) {
node->next = *list;
*list = node;
}
```
```graphviz
digraph G
{
rankdir=LR;
{
listp [label="*list"];
listpp [label="list"];
n [label="node"];
node_t [shape=record,label="{<data> *node| <ref> next}"];
list [shape=record,label="{<data> **list| <ref> next}"];
other [shape=record,label="..."];
n->node_t;
listpp->listp;
listp->list [color=blue];
list->other
}
}
```
```graphviz
digraph G
{
rankdir=LR;
{
listp [label="*list"];
listpp [label="list"];
n [label="node"];
node_t [shape=record,label="{<data> *node| <ref> next}"];
list [shape=record,label="{<data> **list| <ref> next}"];
other [shape=record,label="..."];
n->node_t->list->other;
listpp->listp;
listp->node_t [color=red];
}
}
```
再來 `list_concat` **負責將兩個 list 連接起來**:
```cpp
static inline void list_concat(node_t **left, node_t *right) {
while (*left)
left = &((*left))->next;// LLL
*left = right;
}
```
在這個遞迴式的 `quicksort` 函式中,會先以 list 中的第一個節點當作 pivot,將所有比 pivot 小的值連接到 `left` list 上,而比較大的則會接到 `right` list 上,然後再對這兩個 list 遞迴做 quick sort,最後再將 left, pivot, right 連接起來。
### Random 問題
`long int random(void)` 是屬於 stdlib.h 標準函式庫中的函式,該函式根據 `seed` 去 table 找大小為 31 的 long int 的序列的其中一個值來回傳,但這個 seed 若沒有被指定時會自動餵入 1,也因此使得 random 每次回傳的亂數看似是固定的。
有一個常見的解決方法是使用 `void srandom(unsigned int seed)` 來填入 seed,填入的 seed 可以用一個**隨時**變化的值(譬如時間),如此一來 random 回傳的值所屬的序列便會隨時間變換,每次回傳的值的序列也不一樣。
### Optimized q-sort
將 `__node` 結構多一個指標以指向前一個節點:
```cpp=
typedef struct __node {
int value;
struct __node *next;
struct __node *prev;
} node_t;
```
有改寫過後的函式變成:
`list_make_node_t`
```cpp
node_t *list_make_node_t(node_t *list, int value)
{
node_t *n = malloc(1 * sizeof(node_t));
if (!n) {
assert(n && "malloc error");
return NULL;
}
n->value = value;
n->next = NULL;
n->prev = NULL;
if (!list) {
return n;
} else {
node_t *tmp = list;
while (tmp->next) {
tmp = (tmp->next);
}
tmp->next = n;
n->prev = tmp;
return list;
}
}
```
###### tags: `linux2021`