# 2021q1 Homework1 (quiz1)
contributed by < `IzsKon` >
## 解釋上述程式運作原理,搭配 Graphviz,比照 Linked List 練習題 在 HackMD 筆記上視覺化展現
**題目**
```c=
static inline void list_concat(node_t **left, node_t *right) {
while (*left)
LLL;
*left = right;
}
```
LLL = ?
a) ```left = left->next```
b) ```left = (*left)->next```
c) ```left = &((*left)->next)```
d) ```*left = (*left)->next```
**答: c**
**解釋:**
函式`list_concat`功能在串接(concatenate)兩個 linked list,while 迴圈的目的在於找到`left`的結尾(NULL)。`left`的型態為指標的指標,因此用`*left`取得下一個 node ,並將下一個 node 的位置(`&`)存入`left`
```graphviz
digraph graphname{
node[shape=box]
list[shape=plaintext]
rankdir=LR
{
rankdir = LR
head[shape=plaintext]
B
C
D
…[shape=plaintext]
}
{
rank="same";
list->head->A
}
A->B->C->D->…
}
```
```graphviz
digraph graphname{
node[shape=box]
list[shape=plaintext]
rankdir=LR
{
rankdir = LR
head[shape=plaintext]
B
C
D
…[shape=plaintext]
}
{
rank="same";
head->A
}
{
rankdir = EW
rank="same";
list->B
}
A->B->C->D->…
}
```
```graphviz
digraph graphname{
node[shape=box]
list[shape=plaintext]
rankdir=LR
{
rankdir = LR
head[shape=plaintext]
B
C
D
…[shape=plaintext]
}
{
rank="same";
head->A
}
{
rank="same";
list->C
}
A->B->C->D->…
}
```
**題目**
```c=
static inline void list_add_node_t(node_t **list, node_t *node_t) {
node_t->next = *list;
*list = node_t;
}
```
```c=
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);
}
quicksort(&left);
quicksort(&right);
node_t *result = NULL;
list_concat(&result, left);
CCC;
*list = result;
}
```
AAA = ?
a) ```&pivot```
b) ```pivot```
c) ```&left```
d) ```left```
e) ```&right```
f) ```right```
**答: e**
BBB = ?
a) ```&pivot```
b) ```pivot```
c) ```&left```
d) ```left```
e) ```&right```
f) ```right```
**答: c**
CCC = ?
(a) ```list_concat(&result, right)```
b) ```list_concat(&result, pivot); list_concat(&result, right)```
c) ```list_concat(&result, right); list_concat(&result, pivot)```
**答: b**
**解釋:**
這裡就是實作 quicksort 的程式碼。quiksort 的運作方式是將數字以 pivot 為準,切割為數值較大的一邊和數值較小的一邊,然後對每一段繼續做相同的是直到無法再切割,最後再將所有東西合併。
`CCC`就是在將每個 linked list 串接的步驟。因為要求是由小排到大,因次串接的的順序是`left`,`pivot`然後`right`。
while 迴圈的部分就是在將 list 切成兩個,一個是數值比`value`還小的`left`,一個是數值比`value`還大的`right`。
`list_add_node_t`接受的第一個參數型態是`node_t **`,是一個指標的指標,因此這裡放的應該是指`right`和`left`的位置`&right`和`&left`。
- 一開始的linlked list
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
3->2->5->1->4
}
```
- 將list切割成數值較小的`left`與數值較大的`right`
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
right[shape=plaintext]
left[shape=plaintext]
pivot[shape=plaintext]
}
left->2->1
pivot->3
right->5->4
}
```
- 將`left`與`right`繼續做切割,直到一方為NULL(以下以繼續切割`left`為例)
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
left[shape=plaintext]
}
left->2->1
}
```
切割後:
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
right[shape=plaintext]
left[shape=plaintext]
pivot[shape=plaintext]
NULL[shape=plaintext]
}
left->1
pivot->2
right->NULL
}
```
- 若一方為NULL,則依`left`,`pivot`,`right`的順序將它們與最終結果`result`串接
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
result[shape=plaintext]
}
result->1->2
}
```
### 測試程式使用到 random,多執行幾次可發現輸出結果相仿,請修正,並引入其他 Pseudorandom number generator
因為`random()`並不是真正的隨機,而是公式算出來的,因此每次一開始的亂數種子相同,得到的結果也會相同,為了避免每次執行結果相同,可以使用時間來做為亂數種子```srandom(time(NULL));```
不過因為`time()`的時間精度為秒,因此在同一秒內執行仍然會得出相同結果。有幾種改進方式:
1. 增加時間的精度,使用`gettimeofday()`得到亂數種子,使時間精度詳細到微秒。
2. 使用別的 Pseudorandom number generator,例如`arc4random_uniform()`是使用 entrophy 作為亂數種子。
## 參考 Optimized QuickSort — C Implementation (Non-Recursive) 並重寫上述 quick sort 程式碼,避免使用遞迴呼叫
**參考的程式碼:**
```c=
bool quickSort(int *arr, int elements) {
#define MAX_LEVELS 1000
int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R ;
beg[0]=0; end[0]=elements;
while (i>=0) {
L=beg[i]; R=end[i]-1;
if (L<R) {
piv=arr[L]; if (i==MAX_LEVELS-1) return NO;
while (L<R) {
while (arr[R]>=piv && L<R) R--; if (L<R) arr[L++]=arr[R];
while (arr[L]<=piv && L<R) L++; if (L<R) arr[R--]=arr[L]; }
arr[L]=piv; beg[i+1]=L+1; end[i+1]=end[i]; end[i++]=L; }
else {
i--; }}
return YES; }
```
原本使用遞迴的quicksort是利用呼叫function會出現的stack來儲存資料,上面的程式碼將那些隱藏的stack改成了用自己的stack`beg[]`和`end[]`來儲存,可以自行控制stack的最大深度,同時也減少了每次呼叫function所花費的時間。
**實作的程式碼:**
```c=
bool qsort(node_t **list)
{
#define MAX_LEVELS 1000
node_t *stack[MAX_LEVELS] = {*list};
node_t *result = NULL;
int i = 0;
while (i >= 0) {
node_t *left = NULL, *right = NULL, *pivot = NULL;
if (stack[i] != NULL && stack[i]->next != NULL){
if (i == MAX_LEVELS - 1)
return false;
pivot = stack[i];
int value = pivot->value;
node_t *p = pivot->next;
pivot->next = NULL;
while (p) {
node_t *n = p;
p = p->next;
list_add_node_t(n->value > value ? &right : &left, n);
}
stack[i++] = right;
stack[i++] = pivot;
stack[i] = left;
}
else {
if (stack[i] != NULL)
list_concat(&result, stack[i]);
i--;
}
}
*list = result;
return true;
}
```
實作的部分和參考有一些不同部分。參考程式碼式針對陣列去做排序,而非這裡使用的 singly linked list,因此不適合從後面的數字往前找,所以只有使用到一個`stack`,而非前面的`beg[]`和`end[]`。
這裡實作的方式前半部與使用遞迴的方式相同,就是將 linked list 以`pivot`切割成`right`與`left`,考慮到最後的`list_concat()`是將`result`放在`list`的左邊,因此依`right`、`pivot`、`left`的順序將其放入`stack`,使得`left`會最先被串接到`result`,其次才是`pivot`、`left`。
**範例:**
- 初始的 linlked list
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
head[shape=plaintext]
}
head->3->2->5->1->4
}
```
stack:
|head| | | | |...|
|----|-|-|-|-|---|
- 做初次切割
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
right1[shape=plaintext]
left1[shape=plaintext]
pivot1[shape=plaintext]
}
left1->2->1
pivot1->3
right1->5->4
}
```
stack:
|right1|pivot1|left1| | |...|
|------|------|-----|-|-|---|
- pop `left1`,繼續切割
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
right2[shape=plaintext]
left2[shape=plaintext]
pivot2[shape=plaintext]
NULL[shape=plaintext]
}
left2->1
pivot2->2
right2->NULL
}
```
stack:
|right1|pivot1|right2|pivot2|left2|...|
|------|------|------|------|-----|---|
- 直到 pop 出來的`list == NULL` 或是 `list->next == NULL`(只剩一個元素),將 pop 出來的 list 串接到`result`
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
result[shape=plaintext]
}
result->1
}
```
stack:
|right1|pivot1|right2|pivot2| |...|
|------|------|------|------|-|---|
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
result[shape=plaintext]
}
result->1->2
}
```
stack:
|right1|pivot1|right2| | |...|
|------|------|------|-|-|---|
```graphviz
digraph graphname{
node[shape=box]
rankdir=LR
{
result[shape=plaintext]
}
result->1->2
}
```
stack:
|right1|pivot1| | | |...|
|------|------|-|-|-|---|