# 2020q3 Homework1 (quiz1)
contributed by < ptzling310 >
- 假設
1. 單向 linked list
2. No circular
## 事前作業
由於對 Pointer 不熟悉, 所以另外寫一個 Note:[筆記連結](https://hackmd.io/@ptzling310/pointer)。
- [x] Pointer
- [x] Pointer to pointer
- [ ] 用Pointer 當parameter
:::info
pointer to pointer 指標再移動時,我自己會容易搞混( 有的是移動到 node 的 next , 有的移動到下一個 node。
:::
## Homework
- [x] 解釋上述程式運作原理,搭配 Graphviz,比照 Linked List 練習題 在 HackMD 筆記上視覺化展現
:::info
我其實想把指標指向 node 畫直的, node 間畫成橫的,但目前都是全部箭頭橫向。
要再找時間改正( 我認為我自己這樣畫會比較好區分指標跟 node) 。
:::
- [x] 函式 swap_pair 和 reverse 對於指標的操作方式顯然異於 add_entry 及 remove_entry,需要額外做 head = ... 的更新,請用指標的指標來改寫,並避免回傳指標
- [x] 以遞迴改寫上述的 reverse,注意,你可能因此需要建立新的函式,如 rev_recursive,隨後在 reverse 函式中呼叫 rev_recursive;
- [ ] 針對 singly-linked list 的節點,實作 [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle),你應該儘量降低記憶體的使用量;
## 原程式碼
```c=
#include <stdio.h>
#include <stdlib.h>
typedef struct __node {
//Node 儲存兩個東西
int value; //內容
struct __node *next; //下一個節點
} node_t;
void add_entry(node_t **head, int new_value)
{
//建一個 pointer: indirect 和 head 指向同一個 node
node_t **indirect = head;
//new NODE
node_t *new_node = malloc(sizeof(node_t));
new_node->value = new_value;
new_node->next = NULL;
//要找到尾端插入 node
assert(new_node);
while (*indirect)
indirect = &(*indirect)->next;
//找到則插入
*indirect = new_node;
}
node_t *find_entry(node_t *head, int value)
{
node_t *current = head;
for (; current && current->value != value; current = current->next)
/* interate */;
return current;
}
void remove_entry(node_t **head, node_t *entry)
{
node_t **indirect = head;
while ((*indirect) != entry)
indirect = &(*indirect)->next;
*indirect = entry->next;
free(entry);
}
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;
}
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
cursor->next = head; head->next->next = cursor;
head = next;
}
return cursor;
}
void print_list(node_t *head)
{
for (node_t *current = head; current; current = current->next)
printf("%d ", current->value);
printf("\n");
}
int main(int argc, char const *argv[])
{
node_t *head = NULL;
print_list(head); //NULL
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); //72-101-108-109-110-111
node_t *entry = find_entry(head, 101);//從 head 找 101 在地node
remove_entry(&head, entry);
entry = find_entry(head, 111);
remove_entry(&head, entry);
print_list(head);
/* swap pair.
* See https://leetcode.com/problems/swap-nodes-in-pairs/
*/
//head = swap_pair(head);
swap_pair(&head);
print_list(head);
//head = reverse(head);
reverse(&head);
print_list(head);
return 0;
}
```
結果
```c=
72 101 108 109 110 111
72 108 109 110
108 72 110 109
109 110 72 108
```
## Struct
### node_t
定義structure,別名node_t
```c=
typedef struct __node {
//Node 儲存兩個東西
int value; //內容
struct __node *next; //下一個節點
} node_t;
```
圖示
```graphviz
digraph structs
{
node[shape=record]
struct1 [label="<f0> value|<f1> next"];
}
```
- 若使用別名,則再建立NODE1: ```node_t NODE1```
```c=
typedef struct __node {
//Node 儲存兩個東西
int value; //內容
struct __node *next; //下一個節點
};
```
若**無**使用別名,則再建立NODE1: `struct __node NODE1`
## Function
在 main() function中:
`node_t *head = NULL;`
表示一個 pointer: head 指向NULL。之後這個 head 會一直指向 list 中的第一個 node。
```
head → NULL
```
---------------------------------------------------
### Add_entry
新增一個 node
#### Paramer
1. `node_t **head`: 想成 `node_t **head = &head(main 中設定的那個 head)`
2. `int new_value`: node 要存的值
#### Code
```c=
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;
}
```
#### 理解
會先建立一個 potiner to pointer: indirect 指向 head 所指的 node。
再來分配空間、值給 new node。最後利用 indirect 走訪 list ,找到 new node 的插入位址(NULL的地方),再將 new node 丟進去。
#### Graph
##### 1. 情況一: 新增**第一個**node
```c
node_t *head = NULL;
```
```graphviz
digraph structs {
node [shape=record];
struct1 [label="{ head |{ <value>0x0 }}"];
NULL1[label="...",shape=plaintext]
struct1:value -> NULL1
}
```
```c
add_entry(&head, 72)
```
main 中會呼叫 `add_entry(node_t **head, int new_value)`,設定參數
`note_t **head= &head(main)`以及`int new_value=72`,為了方便區分,來自 main 的 head 記為 head(main)。
```graphviz
digraph structs {
node [shape=record];
head_main [label="{ head(main) |{ <addr>0x0 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
NULL1[label="...",shape=plaintext]
head_main:addr -> NULL1
head->head_main;
}
```
```c=1
node_t **indirect = head;
```
再來進入 add_entry 的 function 中。 為了 traversal list , 所以設一個指標來記錄。
`**indirect = head = &head(main)`
```graphviz
digraph structs {
node [shape=record];
head_main [label="{ head(main) |{ <addr>0x0 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
indirect [label="{ indirect |{ &head}}"];
NULL1[label="...",shape=plaintext]
head_main:addr -> NULL1
head->head_main;
indirect->head_main
}
```
```c=3
node_t *new_node = malloc(sizeof(node_t));
new_node->value = new_value;
new_node->next = NULL;
assert(new_node);
```
這部分用來新增 node ,並設定應該有的值。
```graphviz
digraph structs {
node [shape=record];
head_main[label="{ head(main) |{ <addr> &NULL }}"];
head[label="{ head |{ <addr>&head(main) }}"];
indirect [label="{ indirect |{ &head}}"];
NULL1[label="NULL",shape=plaintext]
head_main->NULL1
indirect->head_main;
head->head_main
rankdir=LR
{
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
n1:next->NULL_n1
}
}
```
```c=9
while (*indirect)
indirect = &(*indirect)->next;
*indirect = new_node;
```
藉由 indirect 去查看他所指向的位址所指向的是否為NULL (也就是 head(main) 的值),找到後將 indirect 所指的位址的值放入 new node。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
n1:next->NULL_n1
}
head_main[label="{ head(main) |{ <addr> &72 }}"];
head[label="{ head |{ <addr>&head(main) }}"];
indirect [label="{ indirect |{ &72}}"];
//NULL1[label="NULL",shape=plaintext]
head_main->n1;
head->head_main;
indirect->head_main;
}
```
##### 情況二:在有 node 存在的 list 中插入
承情況一,目前 list 中已經有 72 這個 node,現在要插入 101。
```c
add_entry(&head, 101);
```
main 中會呼叫 add_entry(node_t **head, int new_value),設定參數
note_t **head= &head(main)以及int new_value=10為了方便區分,來自 main 的 head 記為 head(main)。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
head->head_main;
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
n1:next->NULL_n1
head_main:addr->n1
}
}
```
```c=1
node_t **indirect = head;
```
再來進入 add_entry 的 function 中。 為了 traversal list , 所以設一個指標來記錄。
`**indirect = head = &head(main)`
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
head->head_main;
indirect [label="{ indirect |{ &head(main)}}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
indirect->head_main;
n1:next->NULL_n1
head_main:addr->n1;
}
}
```
```c=3
node_t *new_node = malloc(sizeof(node_t));
new_node->value = new_value;
new_node->next = NULL;
assert(new_node);
```
這部分用來新增 node ,並設定應該有的值。
```graphviz
digraph structs {
node [shape=record];
n2[label=" {101 | <next> }"];
NULL_n2[label="NULL",shape=plaintext];
n2:next->NULL_n2
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
head->head_main;
indirect [label="{ indirect |{ &head(main)}}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
n1:next->NULL_n1
head_main:addr->n1;
indirect->head_main;
}
}
```
```c=9
while (*indirect)
indirect = &(*indirect)->next;
*indirect = new_node;
```
由於此 list 中已有node,故 indirect 必須往前。
```graphviz
digraph structs {
node [shape=record];
n2[label=" {101 | <next> }"];
NULL_n2[label="NULL",shape=plaintext];
n2:next->NULL_n2
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
head->head_main;
indirect [label="{ indirect |{ &head(main)}}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
NULL_n1[label="NULL",shape=plaintext];
n1:next->NULL_n1
head_main:addr->n1;
indirect->NULL_n1;
}
}
```
最後找到 NULL ,插入new node。
```graphviz
digraph structs {
node [shape=record];
n2[label=" {101 | <next> }"];
NULL_n2[label="NULL",shape=plaintext];
n2:next->NULL_n2
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
head->head_main;
indirect [label="{ indirect |{ &head(main)}}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n1:next->n2;
head_main:addr->n1;
indirect->n2;
}
}
```
---------------------------------------------------
### Find_entry()
去找到 list 中的某個數所在的位址,並且回傳
- Code
```c=
node_t *find_entry(node_t *head, int value)
{
node_t *current = head;
for ( ; current && current->value != value; current = current->next)
/* interate */;
return current;
}
```
- 理解
建立一個 pointer: current 指向 head,以記錄 list 第一個 node。
進入 for 迴圈,當 current 所指 node 有值,但不為所尋找的值,則往下一個 node 前進。
- 目前 list 狀態
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n5[label=" {110 | <next> }"];
n6[label=" {111 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->n5;
n5:next->n6;
n6:next->NULL;
head_main:addr->n1;
}
}
```
```c
node *entry = find_entry(head,101);
```
在 main 中,利用 pointer:entry 來記錄101的 address。
呼叫 `find_entry(head,101)`時,設定參數`node_t *head=head(main)`以及`int value=101`。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n5[label=" {110 | <next> }"];
n6[label=" {111 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->n5;
n5:next->n6;
n6:next->NULL;
head_main:addr->n1;
head->n1;
}
}
```
```c=3
node_t *current = head;
```
建一個 poninter:current 指向 head 所指向的 node。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&72 }}"];
current [label="{ current |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n5[label=" {110 | <next> }"];
n6[label=" {111 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->n5;
n5:next->n6;
n6:next->NULL;
head_main:addr->n1;
head->n1;
current->n1;
}
}
```
```c=4
for ( ; current && current->value != value; current = current->next)
/* interate */;
return current;
```
當 current 所指向的位址非 NULL 且 current 所指向的值不為要找的,則往前一個 node。找到該值之後,回傳 `current`,在此例就是回傳`&101`,故在 main 中, `node_t *entry = &101`。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&72 }}"];
current [label="{ current |{ <addr>&101 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n5[label=" {110 | <next> }"];
n6[label=" {111 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->n5;
n5:next->n6;
n6:next->NULL;
head_main:addr->n1;
head->n1;
current->n2;
}
}
```
---------------------------------------------------
### Remove_entry
- 目前 list 狀態
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
entry [label="{ entry |{ <addr>&101 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n5[label=" {110 | <next> }"];
n6[label=" {111 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->n5;
n5:next->n6;
n6:next->NULL;
head_main:addr->n1;
entry->n2;
}
}
```
- Code
```c=
void remove_entry(node_t **head, node_t *entry)
{
//指向 head 所指的 node
node_t **indirect = head;
while ((*indirect) != entry)
indirect = &(*indirect)->next;
*indirect = entry->next;
free(entry);
}
```
- 理解
建立一個 indirect 指向 head 所指 node (第一個 node )。
當 indirect 與 entry 指向不同 node 時,則在繼續確認下一個 node,
直到找到後,將 indirect 指向 entry 的下一個 node,便可釋放 entry 所指 node。
```c
remove_entry(&head,entry);
```
在 main 中呼叫 remove_entry function,故設定參數`node_t **head = head(main)`及`node_t *entry = entry)`。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
entry_main [label="{ entry(main) |{ <addr>&101 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
entry [label="{ entry |{ <addr>&101 }}"];
more[label="...",shape=plaintext];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->more;
head_main:addr->n1;
entry_main->n2;
head->head_main;
entry->n2;
}
}
```
```c=4
node_t **indirect = head;
```
建一個 pointer to pointer:indirect 指向 head 。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
entry_main [label="{ entry(main) |{ <addr>&101 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
entry [label="{ entry |{ <addr>&101 }}"];
indirect [label="{ indirect |{ <addr>&head(main) }}"];
more[label="...",shape=plaintext];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->more;
head_main:addr->n1;
entry_main->n2;
head->head_main;
entry->n2;
indirect->head_main;
}
}
```
```c=6
while ((*indirect) != entry)
indirect = &(*indirect)->next;
```
`*indirect` 意為 indirect所指的 node 所存的值不與 entry 相同(此例中,要為 &101 )。
`indirect = &(*indirect)->next` 第一次時看我會把它會錯意,認為是 indirect 直接指向 head 的下一個node (也就是指向 101)。但在接下來一行程式會發現`*indirect = entry->next;`執行起來怪怪的。 所以這裡其實應該是指向**72的 next** 。所以下圖,`*indirect`的值就為 &101了! while迴圈結束。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
entry_main [label="{ entry(main) |{ <addr>&101 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
entry [label="{ entry |{ <addr>&101 }}"];
indirect [label="{ indirect |{ <addr> 72-next }}"];
more[label="...",shape=plaintext];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n1:next->n2;
n2:next->n3;
n3:next->n4;
n4:next->more;
head_main:addr->n1;
entry_main->n2;
head->head_main;
entry->n2;
indirect->n1:next;
}
}
```
```c=9
*indirect = entry->next;
```
將 indirect 所指向的位址的**值**改為 entry 所指的下一個位址。
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
entry_main [label="{ entry(main) |{ <addr>&101 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
entry [label="{ entry |{ <addr>&101 }}"];
indirect [label="{ indirect |{ <addr> 72-next }}"];
more[label="...",shape=plaintext];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {101 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n1:next->n3;
n2:next->n3;
n3:next->n4;
n4:next->more;
head_main:addr->n1;
entry_main->n2;
head->head_main;
entry->n2;
indirect->n1:next;
}
}
```
```c=10
free(entry);
```
```graphviz
digraph structs {
node [shape=record];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
indirect [label="{ indirect |{ <addr> 72-next }}"];
more[label="...",shape=plaintext];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n3[label=" {108 | <next> }"];
n4[label=" {109 | <next> }"];
n1:next->n3;
n3:next->n4;
n4:next->more;
head_main:addr->n1;
head->head_main;
indirect->n1:next;
}
}
```
---------------------------------------------------
### Swap_pair
- 目前 list 狀態
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 |<next> }"];
n2[label=" {108 |<next> }"];
n3[label=" {109 |<next> }"];
n4[label=" {110 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
}
}
```
:::spoiler 未改用void之程式
```c=
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;
}
```
:::
- Code 之改寫
想法是:要在 function 裡面改變 pointer 的值。所以利用 pointer to pointer 來控制 main function 中的 head。
```c=
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;
}
}
```
- 理解
從 link list 的第一個 node 開始,一次交換兩個。
```c
//in main function
swap_pair(&head);
```
在main 呼叫 `swap_pair` 應改寫為這樣,在此設定參數`node_t **head = &head(main)`。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head->head_main;
head_main:addr->n1;
}
}
```
`node_t **node = head`:設定 node 指向 head(main)
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
nod [label="{ node |{ <addr>&head(main) }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head->head_main;
nod->head_main;
head_main:addr->n1;
}
}
```
`*node && (*node)->next`:當 node 所指向的位址及其下一個點的值不為 NULL
`node = &(*node)->next->next)` node 移到下下一個點。
```c=4
node_t *tmp = *node;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
nod [label="{ node |{ <addr>&head(main) }}"];
tmp [label="{ tmp |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head->head_main;
nod->head_main;
head_main:addr->n1;
tmp->n1;
}
}
```
```c=5
*node = (*node)->next;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
nod [label="{ node |{ <addr>&head(main) }}"];
tmp [label="{ tmp |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head->head_main;
nod->head_main;
head_main->n2;
tmp->n1;
}
}
```
```c=6
tmp->next = (*node)->next;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
nod [label="{ node |{ <addr>&head(main) }}"];
tmp [label="{ tmp |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n3;
n2->n3;
n3->n4;
n4->NULL;
head->head_main;
nod->head_main;
head_main->n2;
tmp->n1;
}
}
```
```c=7
(*node)->next = tmp;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head(main) }}"];
nod [label="{ node |{ <addr>&head(main) }}"];
tmp [label="{ tmp |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {72 | <next> }"];
n2[label=" {108 | <next> }"];
n3[label=" {109 | <next> }"];
n4[label=" {110 | <next> }"];
n1->n3;
n2->n1;
n3->n4;
n4->NULL;
head->head_main;
nod->head_main;
head_main->n2;
tmp->n1;
}
}
```
---------------------------------------------------
### Reverse
:::spoiler 未改用void之程式碼
```c=
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
cursor->next = head; head->next->next = cursor;
head = next;
}
return cursor;
}
:::
- 目前 list 狀態
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
}
}
```
- Code 之改寫: void
```c=
void reverse(node_t **head)
{
node_t *cursor = NULL;
while (*head) {
node_t *next = (*head)->next;
(*head)->next = cursor;
cursor = *head;
*head = next;
}
*head = cursor;
}
```
`reverse(&head);`:在 main 中會呼叫 `reverse`,並設定參數`node_t **head = &head`。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head->head_main
}
}
```
```c=3
node_t *cursor = NULL;
```
建立一個 pointer:cursor 指向 NULL。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head }}"];
NULL_c[label="NULL",shape=plaintext];
cursor [label="{ cursor |{ <addr>&NULL }}"];
cursor->NULL_c;
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head->head_main
}
}
```
```c=4
while (*head) {
node_t *next = (*head)->next;
(*head)->next = cursor;
cursor = *head;
*head = next;
}
```
當 head(main) 所指的 node 不為 NULL時,進入 while 迴圈。
`node_t *next = (*head)->next;`:建 pointer:next 指向
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head }}"];
NULL_c[label="NULL",shape=plaintext];
cursor [label="{ cursor |{ <addr>&NULL }}"];
cursor->NULL_c;
next [label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head->head_main
next->n2;
}
}
```
`(*head)->next = cursor; `:將 head 所指向的 node 的 next 設為 cursor 所指。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head }}"];
NULL_c[label="NULL",shape=plaintext];
cursor [label="{ cursor |{ <addr>&NULL }}"];
cursor->NULL_c;
next [label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->NULL_c;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head->head_main
next->n2;
}
}
```
`cursor = *head;`:cursor 更新到 head 所指的node
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head [label="{ head |{ <addr>&head }}"];
NULL_c[label="NULL",shape=plaintext];
cursor [label="{ cursor |{ <addr>&NULL }}"];
next [label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->NULL_c;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head->head_main
next->n2;
cursor->n1;
}
}
```
`*head = next;`:head 往前
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head [label="{ head |{ <addr>&head }}"];
NULL_c[label="NULL",shape=plaintext];
cursor [label="{ cursor |{ <addr>&NULL }}"];
next [label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->NULL_c;
n2->n3;
n3->n4;
n4->NULL;
head_main->n2;
head->head_main
next->n2;
cursor->n1;
}
}
```
- Code 之改寫: recursive
- 想法: Recursive 最主要是要有『自己呼叫自己』,所以第一次執行,是只做好一部份,然後在把剩下未完的部分在重新執行一樣的動作。直到執行到最後一個 node ,就結束。
```c=
node_t *reverse_recur(node_t *prev , node_t *head)
{
//recursive 終止條件
if(!head)
return prev;
node_t *next = head->next;
head->next = prev;
return reverse_recur(head,next);
}
void reverse(node_t **head)
{
*head = reverse_recur(NULL,*head);
}
```
在 main 中`reverse(&head)`會呼叫`reverse(node_t **head)`,設定參數`node_t **head(reverse) = &head(main)`
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head_reverse[label="{ head(reverse) |{ <addr>&head(main) }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head_reverse->head_main;
}
}
```
在 reverse 又會呼叫`*reverse_recur(node_t *prev , node_t *head)`,並且設定參數`node_t *prev = NULL`以及`node_t *head= *head(reverse)`
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head_reverse[label="{ head(reverse) |{ <addr>&head(main) }}"];
head_recur[label="{ head |{ <addr>&head(main) }}"];
prev[label="{ prev |{ <addr>&NULL }}"];
NULL_prev[label="NULL",shape=plaintext];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head_reverse->head_main;
head_recur->head_main;
prev->NULL_prev;
}
}
```
```c=7
node_t *next = head->next;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head_reverse[label="{ head(reverse) |{ <addr>&head(main) }}"];
head_recur[label="{ head |{ <addr>&head(main) }}"];
prev[label="{ prev |{ <addr>&NULL }}"];
NULL_prev[label="NULL",shape=plaintext];
next[label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head_reverse->head_main;
head_recur->head_main;
prev->NULL_prev;
next->n2;
}
}
```
```c=8
head->next = prev;
```
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&108 }}"];
head_reverse[label="{ head(reverse) |{ <addr>&head(main) }}"];
head_recur[label="{ head |{ <addr>&head(main) }}"];
prev[label="{ prev |{ <addr>&NULL }}"];
NULL_prev[label="NULL",shape=plaintext];
next[label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->NULL_prev;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
head_reverse->head_main;
head_recur->head_main;
prev->NULL_prev;
next->n2;
}
}
```
```c=9
return reverse_recur(head,next);
```
retrun 時又在設定參數,`node_t *prev=head`以及`node_t *head=next`。
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&72 }}"];
head_reverse[label="{ head(reverse) |{ <addr>&head(main) }}"];
head_recur[label="{ head |{ <addr>&head(main) }}"];
prev[label="{ prev |{ <addr>&108 }}"];
NULL_prev[label="NULL",shape=plaintext];
next[label="{ next |{ <addr>&72 }}"];
//rankdir=LR;
n1[label=" {108 |<next> }"];
n2[label=" {72 |<next> }"];
n3[label=" {110 |<next> }"];
n4[label=" {109 |<next> }"];
n1->NULL_prev;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n2;
head_reverse->head_main;
head_recur->head_main;
prev->n1;
next->n2;
}
}
```
以此類推,重複上述步驟直到最後一個 node ,便可完成 reverse 。
---------------------------------------------------
### Fisher–Yates shuffle 洗牌算法
- Fisher–Yates shuffle 的概念
假設目前有6個數字要做 Fisher–Yates shuffle,每次從前面的數字中選一個與最後一個數字交換位址(也就是說最後一次的交換是決定最前面兩個位址的值):
| Round |Range |Select | Result |
| -------- | -------- | -------- |-------- |
| Initial || | 1 2 3 4 5 6 |
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
1 -- 2 --3 -- 4 --5 --6
}
```
| Round |Range| Select | Result |
| -------- | -------- | -------- |-------- |
| 1 |1-6| 3|1 2 6 4 5 3|
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
3 [label="3", style=filled, fillcolor=grey]
1 -- 2 --6 -- 4 --5 --3
}
```
| Round |Range| Select | Result |
| -------- | -------- | -------- |-------- |
| 1 | 1-6|3|1 2 6 4 5 ++3++|
| 2 | 1-5|1|5 2 6 4 ++1 3++|
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
3 [label="3", style=filled, fillcolor=grey]
1 [label="1", style=filled, fillcolor=grey]
5 -- 2 --6 -- 4 --1 --3
}
```
| Round |Range| Select | Result |
| -------- | -------- | -------- | -------- |
| 1 | 1-6|3|1 2 6 4 5 ++3++|
| 2 | 1-5|1|5 2 6 4 ++1 3++|
| 3 | 1-4|1|4 2 6 ++5 1 3++|
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
3 [label="3", style=filled, fillcolor=grey]
1 [label="1", style=filled, fillcolor=grey]
5 [label="5", style=filled, fillcolor=grey]
4 -- 2 --6 -- 5 --1 --3
}
```
| Round |Range| Select | Result |
| -------- | -------- | -------- |-------- |
| 1 |1-6| 3|1 2 6 4 5 ++3++|
| 2 | 1-5|1|5 2 6 4 ++1 3++|
| 3 |1-4| 1|4 2 6 ++5 1 3++|
| 4 | 1-3|2|4 6 ++2 5 1 3++|
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
3 [label="3", style=filled, fillcolor=grey]
1 [label="1", style=filled, fillcolor=grey]
5 [label="5", style=filled, fillcolor=grey]
2 [label="2", style=filled, fillcolor=grey]
4 -- 6 --2 -- 5 --1 --3
}
```
| Round |Range| Select | Result |
| -------- | -------- | -------- |-------- |
| 1 | 1-6|3|1 2 6 4 5 ++3++|
| 2 | 1-5|1|5 2 6 4 ++1 3++|
| 3 |1-4| 1|4 2 6 ++5 1 3++|
| 4 | 1-3|2|4 6 ++2 5 1 3++|
| 5 | 1-2|2|4 ++6 2 5 1 3++|
```graphviz
graph graphname {
rankdir=LR
rank="same"
node [shape = circle];
3 [label="3", style=filled, fillcolor=grey]
1 [label="1", style=filled, fillcolor=grey]
5 [label="5", style=filled, fillcolor=grey]
2 [label="2", style=filled, fillcolor=grey]
6 [label="6", style=filled, fillcolor=grey]
4 -- 6 --2 -- 5 --1 --3
}
```
最後剩下一個 node ,也就是執行 `n-1` 回合。
所以,我應該先計算 linked list 內的 node 數目,以利改變 Range 的範圍應設在哪裡。
再來考慮的就是位址交換的問題,所以另寫一個 function 處理選好的兩點 swap 的動作。
預想流程: 算長度(回合數) → 隨機挑選一點 → swap → 回合數 - 1 ……
由於上例是使用從 tail 開始換,為了減少每次都還要從 linked list 尋找 tail 的時間,實作就從頭開始插入。
- 目前 list 狀態
```graphviz
digraph structs {
node [shape=record];
NULL[label="NULL",shape=plaintext];
rankdir=LR
{
head_main [label="{ head(main) |{ <addr>&109 }}"];
//rankdir=LR;
n1[label=" {109 |<next> }"];
n2[label=" {110 |<next> }"];
n3[label=" {72 |<next> }"];
n4[label=" {108 |<next> }"];
n1->n2;
n2->n3;
n3->n4;
n4->NULL;
head_main:addr->n1;
}
}
```
- Code
```c=
void fisher_yates(node_t **head)
{
//先算長度
node_t **tmp = head;
int len=0;
for(; *tmp ; tmp = &(*tmp)->next ){
len+=1;
}
//tmp 回到第一個 node
tmp = head;
//fisher_yates 做 (len - 1) 回合
for (int i = len ; i>1 ; i--){
//選第 r 個來和第一個 node 交換
srand(time(NULL)); //希望亂序結果不同
int r = (rand()%i)+1; //從1~i 中選擇
//開始找第 r 個 node 在哪
node_t **target = tmp;
while(r>1){
target = &(*target)->next;
r--;
}
//交換
int value = (*tmp)->value;
(*tmp)->value = (*target)->value;
(*target)->value = value;
tmp = &(*tmp)->next;
}
}
```
###### tags: `sysprog2020`