# 2020q3 Homework1 (quiz1)
###### tags: `sysprog2020` `homework`
contributed by <`JKChun`>
- [**Linked List** 2020q3 第一周測驗題連結](https://hackmd.io/@sysprog/sysprog2020-quiz1)
- [繳交作業區](https://hackmd.io/@sysprog/sysprog2020-quiz1)
## 程式運作原理
### Function 1 : `add_entry`
- Function 的功能為:
>新增節點,當 linked list 沒有內容時,必須由開發者更新指向開頭的指標。因此實際得到 reference,而非 copy
- Source Code:
```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); #(AA1)
while (*indirect)
indirect = &(*indirect)->next;
*indirect = new_node; #(AA2)
}
```
- Function 的 input 為**指向 node 的指標的指標** `head` 和一個整數型別的變數`new_value`
- **第1行** 建立一個**指向 node 的指標的指標** `indirect` ,並指派 `head` 給`indirect`
```graphviz
digraph graphname{
node[shape=box];
rankdir=LR;
subgraph list{
rankdir = LR;
A[label=head_node];
B[label=node_2];
C[label=node_3];
NULL1[label=NULL,shape=plaintext];
A->B->C->NULL1;
}
subgraph pointer{
rank=LR;
indirect[shape=plaintext, fontcolor=blue, label="**indrect"];
head[shape=plaintext, fontcolor=blue, label="**head"];
head_pointer[shape=plaintext, fontcolor=red];
head->head_pointer[headport=n];
indirect->head_pointer[headport=n];
head_pointer->A;
}
}
```
- **2到4行** 用 `malloc` 分配記憶體空間給 new_node,並指派 `new value` 以及指向 `NULL`
- **AA1** `assert(new_node)` 確定 new_node 有成功建立
- **6到7行** 讓`indriect`指向 `linked_list` 最後一個節點的 `next`
- **AA2** `*indirect = new_node` 最後將 new_node 指派給 `next` ,讓 new_node 變成 `linked_list` 的最後一個 node
### Function 2 : `remove_entry`
- Function 的功能為:
>移除指定節點,指向開頭的指標可能因此變更,所以需要用到 a pointer to a pointer (指標的指標)
- Source Code:
```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);
}
```
- **第1行** 建立一個**指向 node 的指標的指標** `indirect` ,並指派 `head` 給 `indirect`
- **第2到3行** 此部分是要將 `indirect` 指向 `entry node` 前一個 node 的 `next`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> entry|{ <data> 3 | <ref> } "];
node_4 [label="<ptr> node_4|{ <data> 4 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="**head"];
indirect[shape=plaintext, fontcolor=blue, label="**indirect"];
head_pointer[shape=plaintext, fontcolor=red];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_3:ref:c -> node_4:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_4:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
indirect -> node_2:ref
head -> head_pointer[headport=n];
head_pointer -> node_1:ptr;
}
```
- **第4行** 利用 `indirect` 更改 `entry node` 前一個 node 的 `next` ,讓它直接指向 `entry node` 的下一個 node
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> entry|{ <data> 3 | <ref> } "];
node_4 [label="<ptr> node_4|{ <data> 4 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="**head"];
indirect[shape=plaintext, fontcolor=blue, label="**indirect"];
head_pointer[shape=plaintext, fontcolor=red];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_4:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> node_4:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_4:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
indirect -> node_2:ref
head -> head_pointer[headport=n];
head_pointer -> node_1:ptr;
}
```
- **第5行** 用 `free` 釋放 entry 占用的記憶體
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_4 [label="<ptr> node_4|{ <data> 4 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="**head"];
indirect[shape=plaintext, fontcolor=blue, label="**indirect"];
head_pointer[shape=plaintext, fontcolor=red];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_4:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_4:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
indirect -> node_2:ref
head -> head_pointer[headport=n];
head_pointer -> node_1:ptr;
}
```
### Function 3 : `swap_pair`
- Function 的功能為:
>交換一對相鄰的節點,給定 1->2->3->4,則該回傳 2->1->4->3
- Source Code:
```cpp
node_t *swap_pair(node_t *head)
{
for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next (#BB1)) {
node_t *tmp = *node;
*node = (*node)->next; (#BB2)
tmp->next = (*node)->next;
(*node)->next = tmp;
}
return head;
}
```
- 此 function 的功能就是把 `linked list` 裡的 node 按照順序兩兩一組,把順序反過來,所以 `for 迴圈` 的判斷條件很簡單,就是沒有成對的 node 就跳出迴圈
- 也因為是一次操作兩個 node ,所以 **BB1** 一次跳過兩個 node
- 和一般的 swap function 思維一樣,在 `for 迴圈` 裡先額外建一個 `temp` 指標
- 流程如下:
`node_t *tmp = *node;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
node_p[shape=plaintext, fontcolor=blue, label="**node"];
temp[shape=plaintext, fontcolor=blue, label="*temp"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_p -> head;
head -> node_1:ref[headport=n];
temp -> node_1:ref[headport=n];
}
```
`*node = (*node)->next; (#BB2)`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
node_p[shape=plaintext, fontcolor=blue, label="**node"];
temp[shape=plaintext, fontcolor=blue, label="*temp"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_p -> head;
head -> node_2:ptr;
temp -> node_1:ptr;
}
```
`tmp->next = (*node)->next;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
node_p[shape=plaintext, fontcolor=blue, label="**node"];
temp[shape=plaintext, fontcolor=blue, label="*temp"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_p -> head;
head -> node_2:ptr;
temp -> node_1:ref[headport=n];
}
```
`(*node)->next = tmp;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
node_p[shape=plaintext, fontcolor=blue, label="**node"];
temp[shape=plaintext, fontcolor=blue, label="*temp"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_1:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_p -> head;
head -> node_2:ptr;
temp -> node_1:ptr;
}
```
`node = &(*node)->next->next (#BB1)`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
node_p[shape=plaintext, fontcolor=blue, label="**node"];
temp[shape=plaintext, fontcolor=blue, label="*temp"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_1:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_p -> node_1:ref;
head -> node_2:ptr;
temp -> node_1:ptr;
}
```
- 讓`**node` 指向第一個 node 的 next ,這樣做的話,假如有第四個 node ,就可以像上面操作 `*head` 一樣,讓 head node 的 next 可以指向第四個 node
- 最後跳出 `for 迴圈` 並回傳 `head`
### Function 4 : `reverse`
- Function 的功能為:
>將給定的 linked list 其內節點予以反向,即 1->2->3->4,則該回傳 4->3->2->1
- Source Code:
```cpp
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
head->next = cursor; cursor = head; (#CCC)
head = next;
}
return cursor;
}
```
- 流程如下:
`node_t *cursor = NULL;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
cursor[shape=plaintext, fontcolor=blue, label="cursor"];
NULL1[label=NULL,shape=plaintext];
NULL2[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
cursor -> NULL2;
head -> node_1:ref[headport=n];
}
```
`node_t *next = head->next;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
cursor[shape=plaintext, fontcolor=blue, label="cursor"];
next[shape=plaintext, fontcolor=blue, label="*next"];
NULL1[label=NULL,shape=plaintext];
NULL2[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, headport=n];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
cursor -> NULL2;
next -> node_2:ptr;
head -> node_1:ptr;
}
```
`head->next = cursor; cursor = head; (#CCC)`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
cursor[shape=plaintext, fontcolor=blue, label="cursor"];
next[shape=plaintext, fontcolor=blue, label="*next"];
NULL1[label=NULL,shape=plaintext];
NULL2[label=NULL,shape=plaintext];
node_1:ref:c -> NULL2 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, headport=n];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
cursor -> node_1:ptr;
next -> node_2:ptr;
head -> node_1:ptr;
}
```
`head = next;`
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> head|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> node_2|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> node_3|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
cursor[shape=plaintext, fontcolor=blue, label="cursor"];
next[shape=plaintext, fontcolor=blue, label="*next"];
NULL1[label=NULL,shape=plaintext];
NULL2[label=NULL,shape=plaintext];
node_1:ref:c -> NULL2 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, headport=n];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
cursor -> node_1:ptr;
next -> node_2:ptr;
head -> node_2:ptr;
}
```
- 程式的思路是:
- 讓 `cursor` 指向 `*head 指向的 node` 在反轉後要指向的地方,拿上面舉例,`head->node_2->node_3` ,在反轉後 `head node` 應該要指向 NULL ,所以一開始 `cursor` 為指向 NULL 的 pointer ,接著讓 `head->next = cursor` ,再讓 `cursor` 指向 `head node` ,因為 `head node` 是下一個 node `node_2` 要指向的 node
- 讓 `*head` 指向 `自己原本指向的 node` 再下一個順位的 node 也就是 `node_2`
- 一直重複循環到 `*head` 指向 NULL ,然後回傳 `cursor` 結束
## 改寫 `swap_pair` 和 `reverse`
>函式 swap_pair 和 reverse 對於指標的操作方式顯然異於 add_entry 及 remove_entry,需要額外做 head = ... 的更新,請用指標的指標來改寫,並避免回傳指標;
### 改寫 `swap_pair`
- 原本的 code:
```cpp
node_t *swap_pair(node_t *head)
{
for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next (#BB1)) {
node_t *tmp = *node;
*node = (*node)->next; (#BB2)
tmp->next = (*node)->next;
(*node)->next = tmp;
}
return head;
}
```
- pointer to pointer 版:
```cpp
void 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;
}
}
```
- 原本的 code 本來就會將原本指向 head node 的指標指向第二個 node (前面解釋 swap_pair 時有畫出圖), 所以改成 pass by reference 的方式就不必回傳 head pointer 了
### 改寫 reverse
- 原本的 code:
```cpp
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
head->next = cursor; cursor = head; (#CCC)
head = next;
}
return cursor;
}
```
- pointer to pointer 版:
```cpp
void reverse(node_t **head)
{
node_t *cursor = NULL;
while (*head) {
node_t *next = (*head)->next;
(*head)->next = cursor; cursor = *head; (#CCC)
*head = next;
}
*head = cursor;
}
```
## reverse recursive 版
- reverse function
```cpp
void reverse(node_t **head)
{
*head = rev_recursive( *head )
}
```
- recursive function
```cpp
node_t *rev_recursive( node_t *original_head )
{
if( original_head == NULL )
return NULL;
if( original_head->next == NULL )
return original_head;
node_t *rev_head = rev_recursive( original_head->next );
original_head->next->next = original_head;
original_head->next = NULL;
return rev_head;
}
```
recursive function 裡的:
```cpp
if( original_head == NULL )
return NULL;
if( original_head->next == NULL )
return original_head;
node_t *rev_head = rev_recursive( original_head- >next );
return rev_head;
```
是用來找到指向 `linked list` 最後一個 node 的指標並不停的回傳,到最後的 `reverse function` 更新 head 指標
假設有3個 node ,程式會呼叫3次 recursive function
1. 第3次的 recursive function 回傳 C node 的指標( rev_head )
2. 第2次的 recursive function 運用 original_head_2 `讓 C 指向 B,B 指向 NULL` ,回傳 C node 的指標
3. 第1次的 recursive function 運用 original_head_1 `讓 B 指向 A,A 指向 NULL` ,回傳 C node 的指標
最後的 `reverse function` 更新 head 指標
```graphviz
digraph {
rankdir=LR;
node [shape=record];
node_1 [label="<ptr> A|{ <data> 1 | <ref> } "];
node_2 [label="<ptr> B|{ <data> 2 | <ref> } "];
node_3 [label="<ptr> C|{ <data> 3 | <ref> } "];
head[shape=plaintext, fontcolor=blue, label="*head"];
oh1[shape=plaintext, fontcolor=blue, label="*original_head_1"];
oh2[shape=plaintext, fontcolor=blue, label="*original_head_2"];
oh3[shape=plaintext, fontcolor=blue, label="*original_head_3( rev_ head )"];
NULL1[label=NULL,shape=plaintext];
node_1:ref:c -> node_2:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
node_2:ref:c -> node_3:ptr [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, fontcolor=red];
node_3:ref:c -> NULL1 [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
head -> node_1:ptr;
oh1 -> node_1:ptr;
oh2 -> node_2:ptr;
oh3 -> node_3:ptr;
}
```
## 實作 Fisher–Yates shuffle
>針對 singly-linked list 的節點,實作 Fisher–Yates shuffle,你應該儘量降低記憶體的使用量;
- 演算法:(節自維基百科 [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle))
```
To shuffle an array a of n elements (indices 0..n-1):
for i from n−1 downto 1 do
j ← random integer such that 0 ≤ j ≤ i
exchange a[j] and a[i]
```
```cpp
void Shuffle( node_t **head ){
// count the number of node in the list
int num_of_node = 0;
node_t *indirect = *head;
while (indirect){
num_of_node++;
indirect = indirect -> next;
}
srand ( time(NULL) );
node_t *cursor = *head;
node_t *prev_cursor = NULL;
node_t *prev_indirect = NULL;
indirect = *head;
// Fisher–Yates shuffle
for(;num_of_node > 1; --num_of_node){
prev_cursor = NULL;
cursor = head;
prev_indirect = NULL;
indirect = head;
// move cursor and indirect pointers to the node to be changed
// move prev_cursor and prev_indirect pointers to the previous node of
// node to be changed
for(int i = rand() % num_of_node; i > 1; --i){
prev_cursor = cursor;
cursor = cursor -> next;
}
for(int i = num_of_node; i > 1; --i){
prev_indirect = indirect;
indirect = indirect -> next;
}
// down below is swapping two node
if (prev_cursor != NULL)
prev_cursor->next = indirect;
else
*head = indirect;
prev_indirect->next = cursor;
// Swap next pointers
Node *temp = indirect->next;
indirect->next = cursor->next;
cursor->next = temp;
}
}
```
- 用了5個 pointer
- 處理是 head node 要交換的情況
```cpp
// down below is swapping two node
if (prev_cursor != NULL)
prev_cursor->next = indirect;
else
*head = indirect;
```