# 2020q3 Homework1 (quiz1) contributed by < `blueskyson` > ###### tags: `linux2020` ## 目錄 [TOC] ## 延伸問題 1 ### node_t ```cpp= typedef struct __node { int value; struct __node *next; } node_t; ``` ```graphviz digraph g1 { rankdir=LR; node [shape=record]; 1 [label = "{<val>value |<ptr> *next}"]; } ``` ### void add_entry(node_t **head, int new_value) ```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; } ``` #1 宣告新的節點 new_node 指向 NULL,indirect 指向 head ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>...}"] 4 [label = "{<val>n|<ptr>}"] 5 [label = "{<val>new_node|<ptr>}"] 5:ref -> NULL head[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] head->addr->1 indirect->addr 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> NULL } ``` #2 將 indirect 指向第 2 個節點的位址的位址 ```graphviz digraph g2 { rankdir=LR node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>...}"] 4 [label = "{<val>n|<ptr>}"] 5 [label = "{<val>new_node|<ptr>}"] 5:ref -> NULL head[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] addr2[label = "addr", shape=plaintext] indirect->addr->2 head->addr2->1 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> NULL } ``` #n+1 將 indirect 指向第 n 個節點的的位址的位址 ```graphviz digraph g2 { rankdir=LR node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>...}"] 4 [label = "{<val>n|<ptr>}"] 5 [label = "{<val>new_node|<ptr>}"] 5:ref -> NULL head[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] indirect->addr->NULL addr2[label = "addr", shape=plaintext] head->addr2->1 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> NULL } ``` #n+2 讓 indirect 取值,並令其指向 new_node 的位址 ```graphviz digraph g2 { rankdir=LR node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>...}"] 4 [label = "{<val>n|<ptr>}"] 5 [label = "{<val>new_node|<ptr>}"] 5:ref -> NULL head[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] indirect->addr->5 addr2[label = "addr", shape=plaintext] head->addr2->1 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> 5:val } ``` ### node_t *find_entry(node_t *head, int value) ```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; } ``` #1 宣告 current 指向 head 所指的節點 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>desired value |<ptr>}"] 4 [label = "{<val>...}"] current[fontcolor=blue] head[fontcolor=blue] head->1 current->1 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> NULL } ``` #2 比對每個節點的 value 是否為所求,若是就停止搜尋 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>desired value |<ptr>}"] 4 [label = "{<val>...}"] current[fontcolor=blue] head[fontcolor=blue] head->1 current->3 1:ref -> 2:val 2:ref -> 3:val 3:ref -> 4:val 4:ref -> NULL } ``` ### void remove_entry(node_t **head, node_t *entry) ```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 indirect 指向 head 的位址 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 1.5 [label = "{<val>2 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>x|<ptr>}"] 3.5 [label = "{<val>x+1|<ptr>}"] 4 [label = "{<val>...}"] head[fontcolor=blue] entry[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] indirect->addr head->addr->1 entry->3 1:ref -> 1.5:val 1.5:ref -> 2:val 2:ref -> 3:val 3:ref -> 3.5:val 3.5:ref -> 4:val 4:ref -> NULL } ``` #2 indirect 指第二個節點的位址的位址 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 1.5 [label = "{<val>2 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>x|<ptr>}"] 3.5 [label = "{<val>x+1|<ptr>}"] 4 [label = "{<val>...}"] head[fontcolor=blue] entry[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] addr2[label = "addr", shape=plaintext] head->addr2->1 indirect->addr->1.5 entry->3 1:ref -> 1.5:val 1.5:ref -> 2:val 2:ref -> 3:val 3:ref -> 3.5:val 3.5:ref -> 4:val 4:ref -> NULL } ``` #x indirect 指第 x 個節點的位址的位址 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 1.5 [label = "{<val>2 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>x|<ptr>}"] 3.5 [label = "{<val>x+1|<ptr>}"] 4 [label = "{<val>...}"] head[fontcolor=blue] entry[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] addr2[label = "addr", shape=plaintext] head->addr2->1 indirect->addr->3 entry->3 1:ref -> 1.5:val 1.5:ref -> 2:val 2:ref -> 3 3:ref -> 3.5:val 3.5:ref -> 4:val 4:ref -> NULL } ``` #x+1 把 addr 的值換成第 x+1 節點的位址,之後將第 x 節點釋放 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 1.5 [label = "{<val>2 |<ptr>}"] 2 [label = "{<val>...}"] 3 [label = "{<val>x|<ptr>}"] 3.5 [label = "{<val>x+1|<ptr>}"] 4 [label = "{<val>...}"] head[fontcolor=blue] entry[fontcolor=blue] indirect[fontcolor=red] addr[shape=plaintext] addr2[label = "addr", shape=plaintext] head->addr2->1 indirect->addr->3.5 entry->3 1:ref -> 1.5:val 1.5:ref -> 2:val 2:ref -> 3.5:val 3.5:ref -> 4:val 4:ref -> NULL } ``` ### node_t *swap_pair(node_t *head) ```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; } ``` #1 讓 tmp 指向 node 取值 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] tmp[fontcolor=blue] node_[label = "node", fontcolor=red] addr[shape=plaintext] node_->addr->head->1 tmp->1->2->3->4->5 } ``` #2 修改 node 取值 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] tmp[fontcolor=blue] head[fontcolor=blue] node_[label = "node", fontcolor=red] addr[shape=plaintext] node_->addr->head->2 tmp->1 1->2->3->4->5 } ``` #3 修改第一個節點的 next ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] tmp[fontcolor=blue] head[fontcolor=blue] node_[label = "node", fontcolor=red] addr[shape=plaintext] node_->addr->head->2 tmp->1->3 2->3->4->5 } ``` #4 修改第二個節點的 next ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] tmp[fontcolor=blue] head[fontcolor=blue] node_[label = "node", fontcolor=red] addr[shape=plaintext] node_->addr->head->2 tmp->1->3 2->1 3->4->5 } ``` #5 將 node 指向下個節點位址的位址,然後重複執行交換的動作 ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] tmp[fontcolor=blue] head[fontcolor=blue] node_[label = "node", fontcolor=red] addr[shape=plaintext] node_->addr->3 head->2 tmp->3 2->1->3->4->5 } ``` ### node_t *reverse(node_t *head) ```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; } ``` #1 cursor = NULL, next = head->next ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] next[fontcolor=blue] cursor[fontcolor=blue] head->1->2->3->4->5 next->2 cursor->NULL } ``` #2 head->next = cursor ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] next[fontcolor=blue] cursor[fontcolor=blue] head->1->NULL 2->3->4->5 next->2 cursor->NULL } ``` #3 cursor = head ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] next[fontcolor=blue] cursor[fontcolor=blue] head->1->NULL 2->3->4->5 next->2 cursor->1 } ``` #4 head = next ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] next[fontcolor=blue] cursor[fontcolor=blue] head->2 next->2->3->4->5 cursor->1->NULL } ``` #5 next = head->next ,然後持續做直到 head 指向 NULL ```graphviz digraph g2 { rankdir=LR; node [shape=record] 1 [label = "{<val>1 |<ptr>}"] 2 [label = "{<val>2 |<ptr>}"] 3 [label = "{<val>3 |<ptr>}"] 4 [label = "{<val>4 |<ptr>}"] 5 [label = "{<val>...}"] head[fontcolor=blue] next[fontcolor=blue] cursor[fontcolor=blue] head->2->3->4->5 next->3 cursor->1->NULL } ``` ## 延伸問題 2 將 `swap_pair` 改寫為不用回傳指標,調用時,呼叫 `swap_pair(&head)` ```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; } } ``` 將 reverse 改寫為不用回傳指標,調用時,呼叫 reverse(&head) ```cpp= 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; } ``` ## 延伸問題 3 以遞迴改寫 reverse ,調用時,呼叫 reverse(&head) ```cpp= void recur_rev(node_t **head, node_t *cursor) { if (*head == NULL) { *head = cursor; return; } node_t *next = (*head)->next; (*head)->next = cursor; cursor = *head; *head = next; recur_rev(head, cursor); } void reverse(node_t **head) { if (!head) return; recur_rev(head, NULL); } ``` ## 延伸問題 4 針對 singly-linked list 的節點,實作 Fisher–Yates shuffle ,我參考 [Pencil-and-paper method](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Pencil-and-paper_method) 實作 - 首先確認 linked list 的節點多於 1 個,若否就直接退出 - 初始化亂數表,宣告 `int max` 記錄節點的數量、 `node_t *tail` 指向最後一個節點,這兩個變數在第 9 行的迴圈完成初始化 - 接下來執行迴圈 `max` 次,每次會從第 0 到 max - 1 個之中隨機挑選一個數字 `picknum`,然後第 `picknum` 個節點會被移到 `tail` 節點後面並且成為新的 `tail` ,迴圈結束時即完成 shuffle ```cpp= void fisher_yates_shuffle(node_t **head) { if (!head || !(*head) || !(*head)->next) return; srand(time(NULL)); int i, max = 1; node_t *tail; for (tail = *head; tail->next; tail = tail->next) max++; for (i = max; i > 0; --i) { int j, picknum = rand() % i; if (picknum == 0) { tail->next = *head; *head = (*head)->next; tail = tail->next; tail->next = NULL; continue; } node_t *prev; for (j = 0, prev = *head; j < picknum - 1; j++) prev = prev->next; tail->next = prev->next; prev->next = prev->next->next; tail = tail->next; tail->next = NULL; } } ```