owned this note
owned this note
Published
Linked with GitHub
# 2020q3 Homework1 (quiz1)
contributed by <[haoyu0970624763](https://github.com/haoyu0970624763/sysprog21-quiz1)>
延伸問題一:程式運作原理
===
## add_entry
```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;
/* AA1 = "assert(new_node)" */
assert(new_node);
while (*indirect)
indirect = &(*indirect)->next;
/* AA2 = "*indirect = new_node" */
*indirect = new_node;
}
```
` 功能:新增節點在 list 最後面 `
#### **運作原理解說**
1. 動態新增新的 node,將參數 `new_value` 賦值給
`new_node->value`, `new_node->next` 指向 `NULL`
2. 檢查剛剛動態新增的 node 是否成功
3. 將新節點插入到 list 最尾端
`AA1` 跟 `AA2` 選項很少,很好判斷
## find_entry
```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;
}
```
`功能:找到特定值的節點`
## remove_entry
```c
void remove_entry(node_t **head, node_t *entry)
{
node_t **indirect = head;
while ((*indirect) != entry)
indirect = &(*indirect)->next;
*indirect = entry->next;
free(entry);
}
```
`功能:刪除特定節點並釋放空間`
## swap_pair
```c=
node_t *swap_pair(node_t *head)
{
/* BB1 = "node = &(*node)->next->next" */
for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next) {
node_t *tmp = *node;
/* BB2 = "*node = (*node)->next" */
*node = (*node)->next;
tmp->next = (*node)->next;
(*node)->next = tmp;
}
return head;
}
```
`功能:交換 list 中第 i 個節點跟第 i+1 節點的位置(i屬於奇數)`
#### ***運作原理解說***
4-5 行
`node` 為指向 `&head` 的 pointer
`tmp` 先紀錄 node pointer 指向的位址 , 也就是`&list[i]`
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
A[label="node"]
B[label="tmp"]
C[label="head"]
}
{
D[label="list[i]"]
E[label="list[i+1]"]
F[label=""]
}
D->E->F
{
rank=same
B->D
}
{
rank=same
A->C->D
}
}
```
第 8 行 `tmp->next = (*node)->next ;`
由於函式為 swap_pair , 所以 `tmp->next` 應為 `list[i+1]->next`
因此我們可判斷 `BB2` 為 `*node = (*node)->next;`
執行完 `*node = (*node)->next;`
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
A[label="node"]
}
{
B[label="tmp"]
C[label="head"]
}
{
D[label="list[i]"]
E[label="list[i+1]"]
F[label=""]
}
D->E->F
{
rank=same
B->D
}
{
rank=same
A->C->E
}
}
```
執行完 `tmp->next = (*node)->next;`
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
A[label="node"]
B[label="head"]
C[label="list[i+1]"]
}
{
rank=same
A->B->C
}
}
```
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
B[label="tmp"]
}
{
D[label="list[i]"]
E[label=""]
}
D->E
{
rank=same
B->D
}
}
```
執行完 `(*node)->next = tmp;`
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
A[label="node"]
B[label="tmp"]
C[label="head"]
}
{
D[label="list[i+1]"]
E[label="list[i]"]
F[label=""]
}
D->E->F
{
rank=same
B->E
}
{
rank=same
A->C->D
}
}
```
**迴圈下一層**
已經換完前面兩個 , node 變數應跳到 list 中 , 指向後兩個成員的位址 , 進行下一次的 swap
所以 `BB1`為 `node = &((*node)->next->next)`
```graphviz
digraph graphname{
node[shape=box]
rankdir = LR
{
A[label="node"]
C[label="head"]
}
{
D[label="list[i+1]"]
E[label="list[i]"]
F[label=""]
}
D->E->F
{
rank=same
C->D
}
{
rank=same
A->F
}
}
```
## reverse
```c=
node_t *reverse(node_t *head)
{
node_t *cursor = NULL;
while (head) {
node_t *next = head->next;
/* CCC = "head->next = cursor; cursor = head;" */
head->next = cursor; cursor = head;
head = next;
}
return cursor;
}
```
`功能:把 list 反過來連接`
#### ***運作原理解說***
`head` 表走訪 list 過程中的當前節點 , 從 list 最前端開始
`cursor` 表示上次走訪的節點
`next` 表當前節點的下一個節點
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node1"]
B[label="node2"]
C[label="node3"]
}
{
cursor[shape=none]
cursor->NULL
A->B->C
}
{
rank=same;
head[shape=none]
head->A
}
{
rank=same;
next[shape=none]
next->B
}
}
```
從第 8 行 `head = next;`
我們可以推斷出 CCC 應將上次走訪的節點串接在當前節點後面,還有更新上次走訪的節點以利用在下次迴圈中
所以 `CCC` = `head->next = cursor; cursor = head;`
概念如下
執行 `head->next = cursor;`
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node1"]
}
{
A->NULL
}
{
rank=same;
D[shape=none,label="head"]
D->A
}
}
```
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node2"]
B[label="node3"]
}
A->B
{
rank=same;
D[shape=none,label="next"]
D->A
}
}
```
執行 `cursor = head;` 與 `head = next;`
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node1"]
B[label="node2"]
C[label="node3"]
}
{
A->NULL
B->C
}
{
rank=same;
D[shape=none,label="cursor"]
D->A
}
{
rank=same;
next[shape=none,label="next & head "]
next->B
}
}
```
執行迴圈下一層
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node1"]
B[label="node2"]
C[label="node3"]
}
{
A->NULL
B->C
}
{
rank=same;
D[shape=none,label="cursor"]
D->A
}
{
rank=same;
head[shape=none]
head->B
}
{
rank=same;
next[shape=none]
next->C
}
}
```
執行第二次的 `head->next = cursor;`
```graphviz
digraph {
node[shape=box]
rankdir = LR
{
A[label="node1"]
B[label="node2"]
C[label="node3"]
}
{
B->A->NULL
C
}
{
rank=same;
D[shape=none,label="cursor"]
D->A
}
{
rank=same;
head[shape=none]
head->B
}
{
rank=same;
next[shape=none]
next->C
}
}
```
:::warning
**同樣的邏輯繼續下去即可求的反的 list**
:::
## print_list
```c
void print_list(node_t *head)
{
for (node_t *current = head; current; current = current->next)
printf("%d ", current->value);
printf("\n");
}
```
`功能:印出節點的值`
延伸問題二:指標的指標改寫
===
## swap_pair
原本的 `swap_pair` 中的 `head` 在函式內部採用 **call by value** , 改動 `head` 時 , 實際上不會改動到原本的`head` , 是更改複製出來的 `head`
可以用**call by reference** 的方式來避免此狀況
下面是改寫的程式碼
```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;
}
}
```
## reverse
透過 **call by reference** 取代原本 **call by value** 的參數傳遞方式
```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;
}
```
延伸問題三:recursive_reverse
===
將上面的 reverse 函式的概念用遞迴的方式加以實做
```c
void recursive_reverse_step(node_t *curr, node_t *prev, node_t **head)
{
/* next represent the element behind the current element in the list
* if next equal to NULL , then the element we traced is last in the list
*/
node_t *next = curr->next;
if (!next) {
*head = curr;
return;
}
/* prev represent the previous recursive function parameter curr
* it should be linked behind the current element
*/
curr->next = prev;
recursive_rev_step(next, curr, head);
}
void recursive_reverse(node_t **head)
{
if (!head)
return;
recursive_reverse_step(*head, NULL, head);
}
```
延伸問題三: Fisher–Yates shuffle
===
```c
void shuffle(node_t **head) {
srand(time(NULL));
/* Trace the length of the linked list */
int len = 0;
node_t **indirect = head;
while (*indirect) {
len++;
indirect = &(*indirect)->next;
}
/* Append shuffling result to another linked list */
node_t *new = NULL;
node_t **new_head = &new;
while (len) {
int random = rand() % len;
indirect = head;
while (random--)
indirect = &(*indirect)->next;
/* tmp means the node we randomly chosen */
node_t *tmp = *indirect;
/* Shift the element behind tmp to the position of tmp */
*indirect = (*indirect)->next;
/* put the node we chosen to the head of new list */
tmp->next = *new_head;
*new_head = tmp;
len--;
}
*head = *new_head;
}
```
* Step1 : 首先,先走訪整個原始的 linked list,算出原本的 linked list 有多長
* Step2 : 根據 舊linked list 長度生成一個不大於串列長度之隨機亂數並找到相對應節點
* Step3 : 將對應節點從舊 linked list 刪除 , 並將其新增至新的 linked list 的最前面 (也可以選擇新增至串列的最後面,但需要額外紀錄最後面的節點 )
* 回到 stpe2