# 2020 sysprog Homework1 (quiz1)
###### tags: `sysprog`
contributed by < `sciyen` >
[TOC]
## Code explaination
在這邊為方便理解, graph 新增與刪除connection時,
- 以紅色 arrow 代表在前一步中還存在,但是這一步被刪除的 connection
- 以綠色 arrow 代表在這一步被新增的 connection
### Func1: add_entry()
- 目標:於 list 末端插入一個新node。
- 原始程式碼:
```cpp=
void add_entry(node_t **head, int new_value)
{
node_t **indirect = head;
node_t *new_node = malloc(sizeof(node_t));
new_node->value = new_value;
new_node->next = NULL;
assert(new_node);
while (*indirect)
indirect = &(*indirect)->next;
*indirect = new_node;
}
```
- Step1: 利用 `indirect` 指向 `head` ,其中 `head` 是 pointer of pointer ,紀錄第一個 node 所在之 address ,其中 head 可以是指向任意 `(node 的 pointer) 的 pointer` 。
- Step2: malloc 新的記憶體空間,並為其賦值。因此第9行的目的為檢查 malloc 是否成功,若不成功則回報錯誤。
```cpp=5
node_t *new_node = malloc(sizeof(node_t));
new_node->value = new_value;
new_node->next = NULL;
```
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="indirect"];
rankdir=LR;
nodeh [shape=box, label="head\n(*indirect)"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> nodeh:data;
newnode [label="{<data> new_value| <ref> NULL}"];
}
```
- Step3: 利用 for 迴圈直到最後一個 node (因為最後一個 node 的 next 指向 NULL)
```cpp=10
while (*indirect)
indirect = &(*indirect)->next;
```
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="indirect"];
rankdir=LR;
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref> *indirect}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> node2:ref;
newnode [label="{<data> new_value| <ref> NULL}"];
}
```
- Step4: 此時 indirect 為指向 `((最後一個 node) 的 next) 的 pointer` ,因此修改 *indirect 的值將`使最後一個 node 的next` 指向 new_node 的address
```cpp=12
*indirect = new_node;
```
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="indirect"];
rankdir=LR;
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> node2:ref;
node2:ref -> newnode
newnode [label="{<data> new_value| <ref> NULL}"];
}
```
### Func2: find_entry()
- 目標:list中尋找特定 value 的 node ,若沒有 node 符合則回傳 NULL。
- 原始程式碼:
```cpp=
node_t *find_entry(node_t *head, int value)
{
node_t *current = head;
for (; current && current->value != value; current = current->next)
/* interate */;
return current;
}
```
- Step1: 利用 `current` 紀錄目前 `head` ,其中 `head` 可以是list node中的任何一個 node 。
- Step2: 只要目前 node 還存在(還沒到尾巴),並且目前 node 不是目標,就繼續往下一個 node 移動。
```cpp=4
for (; current && current->value != value; current = current->next)
/* interate */;
```
current 指向 node 5 ,但因為與之不符,繼續往下一個點。
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="current"];
rankdir=LR;
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> node0:data [color=green];
}
```
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="current"];
rankdir=LR;
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> node1:data [color=green];
}
```
```graphviz
digraph foo{
node [shape="record"]
node3 [shape="box", label="current"];
rankdir=LR;
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
node3 -> node2:data [color=green];
}
```
假設該 node 值相同,則回傳 current 目前指向之 pointer ,即為 node 63 的 address 。
:::warning
可以觀察到
- `add_entry()` 使用 pointer of pointer of node ,`add_entry()` 是改變一個 next 的指向,其操作對象是一個 pointer ,因此用 pointer of pointer 。
- `find_entry()` 使用 pointer of node , `find_entry()` 是尋找一個 node 的 value ,其操作對象是一個 node 因此使用 pointer of node.
:::
### Func3: remove_entry()
- 目標:list中尋找特定 node ,若找到則釋放其記憶體。
- 原始程式碼:
```cpp=
void remove_entry(node_t **head, node_t *entry)
{
node_t **indirect = head;
while ((*indirect) != entry)
indirect = &(*indirect)->next;
*indirect = entry->next;
free(entry);
}
```
- Step1: 利用 `indirect` 指向 `head` ,其中 `head` 是 pointer of pointer ,紀錄第一個 node 所在之 address ,其中 head 可以是指向任意 `(node 的 pointer) 的 pointer` 。
- Step2: 尋找符合的 `entry address`
```cpp=5
while ((*indirect) != entry)
indirect = &(*indirect)->next;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
entry [shape="box", label="entry"];
indir [shape="box", label="indirect"];
nodeh [shape=box, label="head\n(*indirect)"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
indir -> nodeh:data;
entry -> node2;
}
```
`(indirect 指向的 next) 的 address` 與 `entry 所指向的 address` 不相符,因此繼續往下走。
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
entry [shape="box", label="entry"];
indir [shape="box", label="indirect"];
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>*indirect}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
indir -> node0:ref [color=green];
entry -> node2
}
```
`(indirect 指向的 next) 的 address` 與 `entry 所指向的 address` 相符,皆為 node 63 因此離開迴圈。
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
entry [shape="box", label="entry"];
indir [shape="box", label="indirect"];
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>*indirect}"];
node2 [label="{<data> 63| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data;
indir -> node1:ref [color=green];
entry -> node2;
}
```
- Step3: 接下來把 `entry 的 next`接回指向 entry 的 next,也就是 node 85 的next,以免 node 57 及其後輩們無家可歸。
```cpp=8
*indirect = entry->next;
```
`*indirect` 表示 node 85 的 next ,應指向 entry 原本的 next。
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
entry [shape="box", label="entry"];
indir [shape="box", label="indirect"];
nodeh [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>*indirect}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 57| <ref>}"];
nodeh:ref -> node0:data;
node0:ref -> node1:data;
node1:ref -> node2:data [color="red"];
node2:ref -> node3:data;
indir -> node1:ref;
entry -> node2
node1:ref -> node3:data [color=green];
}
```
- Step4: 最後刪除 `entry` 指向的address,也就是 node 63
```cpp=9
free(entry);
```
### Func4: swap_pair()
- 目標:兩兩節點互換,若總共偶數個節點則剛好交換,若總共奇數個節點則留下最後一個沒有互換。
- 原始程式碼:
```cpp=
node_t *swap_pair(node_t *head)
{
for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next) {
node_t *tmp = *node;
*node = (*node)->next;
tmp->next = (*node)->next;
(*node)->next = tmp;
}
return head;
}
```
- Step1: 因為要倆倆互換,所以一次要跳兩個點,跳之前要先檢查這兩個點是不是都還存在,否則就跳不過去了。
```cpp=3
for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next)
```
- Step2: 將 node 順序互換,使用 tmp 節點暫存。
一開始長這樣:
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node1:ref -> node2:data;
node2:ref -> node3:data;
node3:ref -> node4:data;
nodes -> node1:data [color="green"];
}
```
```cpp=4
node_t *tmp = *node;
*node = (*node)->next;
```
1. `*node` 為原本的head ,因此 `*tmp = *node` 將指向 node 85。
2. `(*node)` 為 node 85 的 address ,因此用`->`指向 next
3. 由於 node 是指向 head 的 `pointer of pointer` ,所以 `*node=` 實際上是對指向 node 85 的 head 重新賦值,在這邊就是重新指向 node 63 ,因為 node 85 的 next 為 node 63 (參考第二點所述)
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node1:ref -> node2:data;
node2:ref -> node3:data;
node3:ref -> node4:data;
nodes -> node2:data [color="green"];
tmp -> node1:data [color="green"];
}
```
因為要讓 node 85 和 node 63 互換順序,因此 node 63 的 next 重新指向為 node 75
```cpp=6
tmp->next = (*node)->next;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node1:ref -> node2:data[color=red];
node2:ref -> node3:data;
node3:ref -> node4:data;
nodes -> node2:data;
tmp -> node1:data;
node1 -> node3[color=green];
}
```
最後,把 node 63 的 next 接上 tmp , tmp 為 Pointer of node,指向 node 85 ,便完成第一組互換。
```cpp=7
(*node)->next = tmp;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node2:ref -> node3:data[color=red];
node3:ref -> node4:data;
nodes -> node2:data;
tmp -> node1:data;
node1 -> node3;
node2:ref ->node1[color=green];
}
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node3:ref -> node4:data;
nodes -> node2:data;
tmp -> node1:data;
node1 -> node3;
node2:ref ->node1[color=green];
}
```
for 迴圈中,最後會更新 node ,將 node 變成指向目前 next next 的 pointer ,也就是 node 85 的 next 的 address ,而 `*node` 實則指向 node 75 。
```cpp=3
node = &(*node)->next->next)
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
tmp [shape=box, label="tmp"];
nodes [shape=box, label="node"];
nodesp [shape=box, label="*node"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
node4 [label="{<data> 97| <ref>}"];
node3:ref -> node4:data;
nodes -> node1:ref[color=red];
nodesp -> node3[color=green];
tmp -> node1:data;
node1 -> node3;
node2:ref ->node1[color=green];
}
```
:::warning
- 注意,因為只有在一開始的時候 node 指向 head 因此只有第一次互換會變更 head 的內容,讓 head 變成 node 63 ,其他時候則是變更`其他 node 的 next` 。
- 總之,就是 node 會令原本指向`奇數 node` 的 `pointer of pointer` 變成指向`偶數 node` ,只是只有第一個是 head ,其他是 next
:::
### Func5: reverse()
- 目標:將 list 內所有 node 指向互換。
- 原始程式碼:
```cpp=
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
head->next = cursor; cursor = head;
head = next;
}
return cursor;
}
```
- Step1: 用迴圈方式,從第一個 node 逐漸換到最後一個 node。
用以下的 list 舉例
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data
cursor -> Null
}
```
```cpp=5
node_t *next = head->next; cursor = head;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node0:ref -> node1:data [color="red"];
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data
cursor -> Null [color="red"];
next -> node1 [color="green"];
node0:ref -> Null [color="green"];
cursor -> head [color="green"];
}
```
拿掉已經刪除的線
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data
next -> node1 [color="green"];
node0:ref -> Null [color="green"];
cursor -> node0 [color="green"];
}
```
將原本指向 node 5 的 head 改為指到下一個 node ,讓 head 可以不斷往前。
```cpp=7
head = next;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data [color="red"];
next -> node1 [color="green"];
node0:ref -> Null [color="green"];
cursor -> node0 [color="green"];
head -> node1 [color="green"];
}
```
拿掉已經刪除的線
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node2:data;
node2:ref -> node3:data;
next -> node1 [color="green"];
node0:ref -> Null [color="green"];
cursor -> node0 [color="green"];
head -> node1 [color="green"];
}
```
會發現 node 5 的 next 重新指向原本 cursor 所指。 head 逐漸往 list 尾端移動。
重複進行以上操作可以得到
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node0:data;
node2:ref -> node1:data [color="green"];
next -> node3 [color="green"];
node0:ref -> Null;
cursor -> node2 [color="green"];
head -> node3 [color="green"];
}
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
cursor [shape=box, label="cursor"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node0:data;
node2:ref -> node1:data;
next -> Null [color="green"];
node0:ref -> Null;
cursor -> node3 [color="green"];
head -> node3 [color="green"];
node3 -> node2 [color="green"];
}
```
最後因為 `head = next;` 因此 head 變為 Null ,因此結束迴圈,最終 cursor 變為新的 head。
## Rewrite with pointer of pointer style
以 `pointer of pointer` 重新實作 `swap_pair()` 與 `reverse()` 程式碼。
### indirect_swap()
- 程式碼:
```cpp=
void indirect_swap(node_t **node){
for (;*node && (*node)->next; node = &(*node)->next->next){
node_t *tmp = *node;
*node = (*node)->next;
tmp->next = (*node)->next;
(*node)->next = tmp;
}
}
```
- 概念大致與 `swap_pair()` 大致相同,差別在於省去 for 迴圈初始化的部分 `node_t **node = &head;` ,取而代之的是,直接以pointer of pointer 的 `node_t **node` 作為參數,此時的 `*node` 即為 head 的內容,因此若修改 `*node` 外部 head 也會隨之改變。
- 其餘 swap 部分的互換方式與 `swap_pair()` 皆相同,可參考 [swap_pair()的解釋](https://hackmd.io/oSo9qqNPS_-ioSXJpfdGPQ?both#Func4-swap_pair)
### indirect_reverse()
- 程式碼:
```cpp=
void indirect_reverse(node_t **node){
node_t *pre = NULL;
while((*node)->next){
node_t *next = (*node)->next;
(*node)->next = pre;
pre = *node;
*node = next;
}
(*node)->next = pre;
}
```
- 初始狀態:此時 head 與 `*node` 皆指向第一個 node(node 5)
- 用一個額外的 pointer 紀錄前一個 node (pre) ,因為當 `*node` 往後移動後,就沒辦法再取得前一個的 address (因為是單向的list)
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head\n(*node)"];
indirect [shape=box, label="node"];
pre [shape=box, label="pre"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data;
indirect->head;
pre->Null;
}
```
- 首先,以 `next` 紀錄下一個 node 的 address ,因為待會要修改目前 node ,也就是 `*node` 的 next ,如果不先記錄下來,修改完 `(*node)->next` 之後,就會移失下一個 node 。
- 修改 `(*node)->next` 變成前一個 node 的 address 。
```cpp=4
node_t *next = (*node)->next;
(*node)->next = pre;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head\n(*node)"];
indirect [shape=box, label="node"];
pre [shape=box, label="pre"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node0:ref -> node1:data[color=red];
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data;
indirect->head;
pre->Null;
next->node1[color=green];
node0:ref -> Null[color=green];
}
```
- 更新 pre 的內容,下一次的 pre 就是目前的 node 。
- `*node` 移動到下一個 node
```cpp=6
pre = *node;
*node = next;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
indirect [shape=box, label="node"];
pre [shape=box, label="pre"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85\n(*node)| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node2:data;
node2:ref -> node3:data;
indirect->next;
pre->node0[color=green];
next->node1;
node0:ref -> Null;
}
```
:::warning
注意 `*indirect` 就是 `head` 目前指向的位置,因此隨著 `*node` 推移,head 也自動變換。
:::
- 重複以上步驟,稍微快轉一下
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
indirect [shape=box, label="node"];
pre [shape=box, label="pre"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node0:data;
node2:ref -> node1:data;
indirect->next;
pre->node2[color=green];
next->node3;
node0:ref -> Null;
}
```
- 此時到最後一個 node `(*node)->next` 為 NULL ,因此跳出迴圈。
- 最後需要重新指向最後一個 node (也是 head)的 next 。
```cpp=9
(*node)->next = pre;
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
indirect [shape=box, label="node"];
pre [shape=box, label="pre"];
next [shape=box, label="next"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
node1:ref -> node0:data;
node2:ref -> node1:data;
indirect->next;
pre->node2;
next->node3;
node0:ref -> Null;
node3:ref->node2[color=green];
}
```
然後就大功告成囉。
## Rewrite `reverse()` with recursive function
```cpp=
void rev_reverse(node_t *pre, node_t **node){
if (!(*node)->next){
(*node)->next = pre;
return;
}
node_t *now = *node;
*node = (*node)->next;
rev_reverse(now, node);
now->next = pre;
}
void rev_reverse_caller(node_t **head){
rev_reverse(NULL, head);
}
```
- 使用 `rev_reverse_caller(node_t **head)` 更新 next 的內容。
- 輸入:
- 前一個 node 的 address
- 目前的 head
- 功能:
- 如果下一個 node 的 next 仍然存在,更新下一個 node 的 next
- 更新完下一個 node 之後,再更新自己的 node
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head\n(*node)"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data;
}
```
- 第一次 recursive
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head\n(*node)"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0[color=green];
pre1->Null[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data;
}
```
- 第二次 recursive
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
head [shape=box, label="head"];
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85\n(*node)| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0[color=green];
pre1->Null[color=green];
now2->node1[color=green];
pre2->node0[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
head -> node0:data;
}
```
- 第二次 recursive
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63\n(*node)| <ref>}"];
node3 [label="{<data> 75| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0[color=green];
pre1->Null[color=green];
now2->node1[color=green];
pre2->node0[color=green];
now3->node2[color=green];
pre3->node1[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
}
```
- 第三次 recursive
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0[color=green];
pre1->Null[color=green];
now2->node1[color=green];
pre2->node0[color=green];
now3->node2[color=green];
pre3->node1[color=green];
now4->node3[color=green];
pre4->node2[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
}
```
- 現在 `(*node)->next` 為 Null ,因此滿足離開條件,不再 recursive
- 將最後一個 node 的 next 接回去 pre 也就是 node 63
```cpp=2
if (!(*node)->next){
(*node)->next = pre;
return;
}
```
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0;
pre1->Null;
now2->node1;
pre2->node0;
now3->node2;
pre3->node1;
"*node"->node3;
pre->node2;
node3:ref->node2:data[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data;
}
```
一個一個回去, now3 的 next 指向 pre3
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0;
pre1->Null;
now2->node1;
pre2->node0;
now3->node2;
pre3->node1;
node3:ref->node2:data;
node2:ref->node1:data[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data;
node2:ref -> node3:data[color=red];
}
```
now2 的 next 指向 pre2
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0;
pre1->Null;
now2->node1;
pre2->node0;
node3:ref->node2;
node2:ref->node1:data;
node1:ref->node0:data[color=green];
node0:ref -> node1:data;
node1:ref -> node2:data[color=red];
}
```
now1 的 next 指向 pre1
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
now1->node0;
pre1->Null;
node3:ref->node2:data;
node2:ref->node1:data;
node1:ref->node0:data;
node0:ref->Null[color=green];
node0:ref -> node1:data[color=red];
}
```
最終
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
node0 [label="{<data> 5| <ref>}"];
node1 [label="{<data> 85| <ref>}"];
node2 [label="{<data> 63| <ref>}"];
node3 [label="{<data> 75\n(*node)| <ref>}"];
Null[shape=plain, label="NULL"]
node3:ref->node2;
node2:ref->node1;
node1:ref->node0;
node0:ref->Null;
}
```
## Fisher–Yates shuffle
### Brief description of `Fisher–Yates shuffle`
這是一個用來打亂 link list 的演算法,藉由隨機抽取 list 中的節點(抽過不可再抽),取得隨機的抽籤順序,形成的抽籤順序即為 shuffle 過的 list。
### Modern Algorithm
假設一開始的 list 長這樣:
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>0|<n1>1|<n2>2|<n3>3|<n4>4|<n5>5|<n6>6|<n7>7}"];
c [label="count=7"];
}
```
現在開始隨機抽一個,假設抽到3,拿3和 `count` 互換,換完之後 count 減一。
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>0|<n1>1|<n2>2|<n3>3|<n4>4|<n5>5|<n6>6|<n7>7}"];
n:n3->n:n7[dir=both, tailclip=false, headclip=false];
c [label="count=7"];
}
```
再抽一個5
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>0|<n1>1|<n2>2|<n3>7|<n4>4|<n5>5|<n6>6|<n7>3}"];
n:n5->n:n6[dir=both, tailclip=false, headclip=false];
c [label="count=6"];
}
```
再抽一個2
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>0|<n1>1|<n2>2|<n3>7|<n4>4|<n5>6|<n6>5|<n7>3}"];
n:n2->n:n5[dir=both, tailclip=false, headclip=false];
c [label="count=5"];
}
```
再抽一個0
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>0|<n1>1|<n2>6|<n3>7|<n4>4|<n5>2|<n6>5|<n7>3}"];
n:n0->n:n4[dir=both, tailclip=false, headclip=false];
c [label="count=4"];
}
```
再抽一個3
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>4|<n1>1|<n2>6|<n3>7|<n4>0|<n5>2|<n6>5|<n7>3}"];
n:n3->n:n3[dir=both, tailclip=false, headclip=false];
c [label="count=3"];
}
```
再抽一個1
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>4|<n1>1|<n2>6|<n3>7|<n4>0|<n5>2|<n6>5|<n7>3}"];
n:n1->n:n2[dir=both, tailclip=false, headclip=false];
c [label="count=2"];
}
```
再抽一個1
```graphviz
digraph foo{
node [shape="record"]
rankdir=LR;
n [label="{<n0>4|<n1>1|<n2>6|<n3>7|<n4>0|<n5>2|<n6>5|<n7>3}"];
n:n1->n:n1[dir=both, tailclip=false, headclip=false];
c [label="count=1"];
}
```
大功告成囉!
程式碼:
```cpp=
int length(node_t *head){
int l=0;
while(head){
l++;
head = head->next;
}
return l;
}
void swap_value(node_t *node1, node_t *node2){
int tmp = node2->value;
node2->value = node1->value;
node1->value = tmp;
}
void shuffle(node_t *head){
srand(time(NULL));
int len = length(head);
for(int i=0; i<len; i++){
int random = rand() % (len - i);
node_t *target = head;
for (int j=0; j<random; j++)
target = target->next;
swap_value(head, target);
head = head->next;
}
}
```
- 利用 `length()` 計算 list 長度。
- 利用 `swap_value()` 交換 node 的內容。
- 由於本例的 list 是單向的,相較於原本演算法是與最後一個元素交換,採取與最前面的元素交換,使得交換的順序與 next 的方向一致。
使用方法:
```cpp=
int main(int argc, char const *argv[])
{
node_t *head = NULL;
print_list(head);
add_entry(&head, 72);
add_entry(&head, 101);
add_entry(&head, 108);
add_entry(&head, 109);
add_entry(&head, 110);
add_entry(&head, 111);
print_list(head);
shuffle(head);
print_list(head);
return 0;
}