--- title: image: description: tags: NCKU Class , 進階電腦系統理論與實作 , 2020q3 --- # 2020q3 Homework1 (quiz1) contributed by < `TsenEN` > * [指標的解說](https://reurl.cc/Q34Rxb) * [題目](https://reurl.cc/R1OyoD) * [共筆區](https://reurl.cc/n0ZLd1) ## Link list structure Definition ```c=1 typedef struct __node { int value; struct __node *next; } node_t ``` ## add_entry() ```c=9 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 while (*indirect) indirect = &(*indirect)->next; AA2 } ``` ### `AA1` - [x] `(a)` `assert(new_node)` - [ ] `(b)` `*indirect = new_node` ### `AA2` - [ ] `(a)` `assert(new_node)` - [x] `(b)` `*indirect = new_node` ### 程式分析 #### Argument ```c=9 void add_entry(node_t **head, int new_value) ``` 為何需要它們? * 你要插入 link list 就需要知道排頭在哪 (head),就好像你排隊也要知道從哪裡開始排隊一樣。 * value 就是你想配置新 node 的值。 #### 配置新空間 ```c=11 node_t **indirect = head; node_t *new_node = malloc(sizeof(node_t)); new_node->value = new_value; new_node->next = NULL; assert(new_node); ``` 為甚麼需要`node_t **indirect = head`,我動 head 就好啦? * pointer to pointer 的用意(注意這邊的 `node_(no.)`並沒有儲存 link list 只是方便好看): * 首先要知道何謂 [Call by value](https://reurl.cc/6lNvEd),另外在 K&R 在其原著 "The C programming language" 中有提到 C 語言只有 <font color = red>Call by value</font> ,可以參考這篇 [c 語言有沒有 call by reference(or call by address) ?](https://reurl.cc/VX8zoQ) :::danger :warning: 共筆提及的超連結 (hyperlink) 的標題應具備一定的資訊量,也就是說儘量用原本的標題或者在原有的標題基礎加以潤飾,避免寫 `link 1` 和 `link 2`,後者無助於閱讀。 另外,共筆書寫不該有太深的縮排 (indention),否則會造成閱讀困難。寫共筆就是為了給人看,讓人指教 :notes: jserv ::: > 好的謝謝老師🙏 > [name=TsenEN] 假設 `0x7fff86ae7208` 是 `head` 的記憶體位址,它放的是 `node_1` 的起始位址,如果今天傳入參數是: | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | * 先看一下這篇 [你所不知道的C語言: linked list 和非連續記憶體操作](https://hackmd.io/@sysprog/c-linked-list) 這篇可以發現新世界! * `node_t *head`,這拿到的直接是一個 head 的記憶體位置`0x7fff86ae7208`(下圖紅色的 head),如果今天是用這個做在 dereference `*indirect` 這樣你會直接存取到 link list 的串列頭(`node_1`),仍然可以做新增的動作,但你需要去做邊界判斷(意思就是你是否在 list 頭尾,在的話需要做不一樣的操作方式),就要像剛剛上面那篇去做判斷! * `node_t **head`,這拿到的會是一個"指著" head 記憶體位置的指標,他的記憶體位置假設是`0x7fff86ae721C`(配合`node_t **indirect = head` 那麼 indirect 就是那個"指著" head 記憶體位置的指標),妳可以透過移動這個 head 指標來操作,這樣的方式可以不用去做邊界判斷。 | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | indirect | 0x7fff86ae721C | 0x7fff86ae7208 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> head; head:p:s -> A; } } ``` assert(new_node) 主要是確認新的 node 是否有配置到,但建議是放14行 malloc 後就確認。 ```c=11 node_t **indirect = head; node_t *new_node = malloc(sizeof(node_t)); assert(new_node); new_node->value = new_value; new_node->next = NULL; ``` #### 找到最尾端 ```c=18 while (*indirect) indirect = &(*indirect)->next; *indirect = new_node; ``` * `*indirect` 他得到的會是一個 node 的位址,所以沒有到 Nil 就不會跳出去。 * `indirect = &(*indirect)->next` 首先 &* 對電腦來說執行順序地位一樣所以要加 "()" ,node 位址`(*indirect)` 的 next 就是下一個 node ,再加 & 取址 assign 給 indirect,就會得到下一個 node 的位址。 * `*indirect = new_node` 因為 indirect 循著 link list 來到最後 Nil 的地方( node_2 所指向的地方 ),所以我們將 new_node assign 給`*indirect` 這個位址作為更新就插到最後面了。 * <font color = 'red' >注意 indirect 指標所指的位址是在那個 node 的 next ,而不是直接跳過去指向那個整個 node </font>,這樣當 while 跑到最後一個 node 去看到它下一個 next 是 Nil,就會跳出 while ,再將 new_node address assign 到 last node 的 next 。 * `add_entry(&head, 108)` #### 1. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> Nil:w; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> head; head:p:s -> A; } } ``` #### 2. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> Nil:w; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> A:next; } } ``` #### 3. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> Nil:w; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> B:next; } } ``` #### 4. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> C; } } ``` ## find_entry() ```c=23 node_t *find_entry(node_t *head, int value) { node_t *current = head; for (; current && current->value != value; current = current->next) /* interate */; return current; } ``` ### 程式分析 #### Argument ```c=23 node_t *find_entry(node_t *head, int value) ``` 程式的用意在於我要找到指定的 node ,那個 node 在哪裡就需要回傳一個記憶體位址,所以我們傳入的引數跟回傳參數都有小細節要注意。 * `*find_entey` 加個 ” * “ 就是因為你要回傳”位址“。 * `node_t *head` 這邊就是個很好的例子( “ ** ” v.s. “ * “),今天我只是需要先確認這個 value 是否有在 list 裡,有的話就傳回位址,“不需要特別去操作指標”所以不用“ ** ”。 #### 尋找目標值 ``` c=26 for (; current && current->value != value; current = current->next) /* interate */; ``` for裡面終止條件為 `current && current->value != value` ,左邊確定是否到 Nil ,右邊在做 vlaue 比對,沒找到就 `current = current->next` 直到碰到 Nil。 #### 回傳為址 ```c=28 return current; ``` * `node_t *entry = find_entry(head, 101);` 關於 struct 在記憶體配置空間的藝術可參考這篇 [C 語言:關於 sizeof 及結構的記憶體對齊](https://reurl.cc/v1klQa)。 假設 node_t 裡面存" value "跟" 記憶體位址 " 都為 4 bytes ,`node_2` 始位址為 `0x7fff86ae7280`。 #### 1. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | current | 0x7fff86ae721C | 0x7fff86ae7240 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | <font size = 3 >value = 101</font> ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] current[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; current:p:s -> A:value; } } ``` #### 2. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | current | 0x7fff86ae721C | 0x7fff86ae7280 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | <font size = 3 >value = 101 current = 0x7fff86ae7280</font> ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] current[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; current:p:s -> B:value; } } ``` 因為 `current->value != value` 不成立跳出 for,`return curret`。 ## remove_entry() <font size = 5>以 remove node_2 為例 entry = 0x7fff86ae7280</font> ```c=31 void remove_entry(node_t **head, node_t *entry) { node_t **indirect = head; while ((*indirect) != entry) indirect = &(*indirect)->next; *indirect = entry->next; free(entry); } ``` ### 程式分析 #### Argument ```c=31 void remove_entry(node_t **head, node_t *entry) ``` 刪除某 link list 的某個 node ,需要 link list (`node_t **head`)跟那個要刪除點的"記憶體位址"(`node_t *entry`)。 #### 尋找 node ```c=31 node_t **indirect = head; while ((*indirect) != entry) indirect = &(*indirect)->next; ``` `while ((*indirect) != entry)` 一開始 indirect 指向的是 `head` ,透過 dereference `(*indirect)` 變成 `node_1` 的起始位址來跟 `entry` 做比較,位址對就跳出 while 不對就將它這個 node 的 next assign 給 `indirect`。 #### 1. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | indirect | 0x7fff86ae721C | 0x7fff86ae7208 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | *indirect = 0x7fff86ae7240 entry = 0x7fff86ae7280 ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> head; head:p:s -> A; } } ``` 0x7fff86ae7240 不等於 0x7fff86ae7280 `(*indirect) != entry` 成立所以繼續執行 while,`indirect = &(*indirect)->next;` 所以 `indirect = 0x7fff86ae7244 `(是 node -> next 的位址不是裡面的值)。 #### 2. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | indirect | 0x7fff86ae721C | 0x7fff86ae7244 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | *indirect = 0x7fff86ae7280 entry = 0x7fff86ae7280 ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> A:next; } } ``` `(*indirect) != entry` 不成立所以跳出 while。 #### 斷開連結 ```c=38 *indirect = entry->next; ``` 找到目標 node 當然我們就要斷開魂結,透過 `indirect` 指著 `node_1` 的 next 來修改。 #### 3. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | indirect | 0x7fff86ae721C | 0x7fff86ae7244 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae72C8 | | .. | .. | .. | | node_3_value | 0x7fff86ae72C8 | 108 | *indirect = 0x7fff86ae7280 entry = 0x7fff86ae7280 entry->next = 0x7fff86ae72C8 ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> C:val; B:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> A:next; } } ``` #### 釋放空間 ```c=39 free(entry); ``` #### 4. | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | indirect | 0x7fff86ae721C | 0x7fff86ae7244 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae72C8 | | .. | .. | .. | | node_3_value | 0x7fff86ae72C8 | 108 | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] indirect[shape=plaintext,fontcolor=blue] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">new_node</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> C:val; C:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; indirect -> A:next; } } ``` ## swap_pair() 程式用意請看 [ Leet code 24. Swap Nodes in Pairs ](https://leetcode.com/problems/swap-nodes-in-pairs/) ```c=42 node_t *swap_pair(node_t *head) { for (node_t **node = &head; *node && (*node)->next; BB1) { node_t *tmp = *node; BB2; tmp->next = (*node)->next; (*node)->next = tmp; } return head; } ``` ### 解題思路 Argument 傳入的是 `head` 指標裡指著 `node_1` 的位址,pointer to pointer (`node`) 是存放 `head` 指標的實體位址,這邊可以思考為何需要 pointer to pointer ? * 當你看到題目心中應該會有兩個做法 (1)交換 Value (2)修改 link next,這邊的做法是 (2) ,先前在 `add_entry()` 提過一下子`* & ** `用法上的差異,透過圖形化應該比較有感覺了吧 ? 這種方式就是為了通過<font color ='red'>存取每個 node 的 next 的實體位址來直接修改它存放的值</font>,這樣就是直接修改 next 所指向的地方。 ### `BB1` - [ ] `(a)` `node = (*node)->next->next` - [ ] `(b)` `*node = (*node)->next->next` - [ ] `(c)` `*node = ((*node)->next)->next` - [ ] `(d)` `*node = &((*node)->next)->next` - [x] `(e)` `node = &(*node)->next->next` - [ ] `(f)` `*node = &(*node)->next->next` ### `BB2` - [ ] `(a)` `node = (*node)->next` - [ ] `(b)` `node = &(*node)->next` - [x] `(c)` `*node = (*node)->next` - [ ] `(d)` `*node = &(*node)->next` - [ ] `(e)` `*node = &((*node)->next)` 假設目前 link list 為 72->101->108->109 目標是 101->72->109->108 先來想看看 BB1,for(`1`;`2`;`3`) 迴圈第`3`個值是我們變數`1` 更新的規律(可能`+ - * / `或者等等其他操作),先看了一下變數`1` 發現是一個存放 pointer 變數 (`node`),那竟然是 link list 想必一定會透過 next 來去更新位址來移動,所以我們可以想喔喔大概是 node = xxx -> next 之類的。 有了 BB1 雛型接著往下看程式很快遇到 BB2,如果大家寫過 swap 兩個變數交換數值的會知道需要額外一個變數來暫存數值。 * 假設今天 A = 72 , B = 101 目標是 A = 101 , B = 72,如果只用兩個變數是無法做到交換的,譬如你寫 A = B (B 把值 assign 給 A), 72 這個數值就會被蓋掉,所以需要一個數值( C )額外暫存。 想法大概是這樣: C = A A = B B = C ```graphviz digraph g { node [shape=none]; A->C; B->A; C->B; } ``` 所以想想 link list 要交換什麼才算換了位置,答案就是 next 所指的方向 ! 所以我們這邊大概有了 BB2 雛型應該是什麼了 ! | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | nodepp | 0x7fff86ae721C | 0x7fff86ae7208 | | .. | .. | .. | | tmp | 0x7fff86ae7220 | 0x7fff86ae7240 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae72C8 | | .. | .. | .. | #### 1. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] nodepp[shape=plaintext,fontcolor=blue] tmp[shape=plaintext,fontcolor=orange] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; D [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_4</TD> <TD PORT="val">109</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> C:val; C:next:c -> D:val; D:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; nodepp -> head; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=LR; tmp:p:s -> A; } } ``` #### 2.(錯誤) 那我們依樣畫葫蘆,下面這樣大錯特錯 ! 因為沒注意到 `head` 已經改變,再者沒有了解到 tmp 跟 node 在這個題目是用來幹嘛的 ! ```c=49 tmp = tmp->next; tmp->next = (*node)->next; (*node)->next = tmp; ``` 照著上面程式碼做 ( nodepp = `node_t **node = &head` 那個 `node` 變數的代稱) | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | | .. | .. | .. | | nodepp | 0x7fff86ae721C | 0x7fff86ae7208 | | .. | .. | .. | | tmp | 0x7fff86ae7220 | 0x7fff86ae7280 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae7280 | | .. | .. | .. | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] nodepp[shape=plaintext,fontcolor=blue] tmp[shape=plaintext,fontcolor=orange] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; D [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_4</TD> <TD PORT="val">109</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> B:val; B:next:c -> B:val; C:next:c -> D:val; D:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> A; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; nodepp -> head; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=LR; tmp:p:s -> A:next; } } ``` 好 ! 看的出來妳玩爆了這個串列。 先釐清其實 `tmp` 在這裡是用來防止 `node_1` 的位址遺失所以先指著,因為當`head` 改成指向 `node_2` 時 `node_1` 就遺失了,有點像上面` C = A ` 。 再來我們要想想真正要換的是什麼 ? ` A = node_1 next 所指向的位址 `、` B = node_2 next 所指向的位址 ` #### 2. 正確的程式碼: ```c=49 *node = (*node)->next; tmp->next = (*node)->next; (*node)->next = tmp; ``` | Name | 記憶體位置 | 先前的Value | 執行完後的 value | | ------------ | -------------- | -------------- |:---------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7240 | 0x7fff86ae7280 | | .. | .. | .. | .. | | nodepp | 0x7fff86ae721C | 0x7fff86ae7208 | 0x7fff86ae7208 | | .. | .. | .. | .. | | tmp | 0x7fff86ae7220 | 0x7fff86ae7240 | 0x7fff86ae7240 | | .. | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae7280 | 0x7fff86ae72C8 | | .. | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | 101 | | .. | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae72C8 | 0x7fff86ae7240 | | .. | .. | .. | .. | | node_3_value | 0x7fff86ae72C8 | 108 | 108 | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] nodepp[shape=plaintext,fontcolor=blue] tmp[shape=plaintext,fontcolor=orange] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; D [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_4</TD> <TD PORT="val">109</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> C:val; B:next:c -> A:val; C:next:c -> D:val; D:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> B; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; nodepp -> head; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; tmp:p:s -> A; } } ``` #### 3.(` node = &(*node)->next->next`、`node_t *tmp = *node;`) node = &(*node)->next->next * `node = 0x7fff86ae7244`(node_1_next 記憶體位置) node_t *tmp = *node; * `tmp = 0x7fff86ae72C8`(node_3_value 記憶體位置) node_3_value 記憶體位置 = node_1_next 記憶體位置 | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7280 | | .. | .. | .. | | nodepp | 0x7fff86ae721C | 0x7fff86ae7244 | | .. | .. | .. | | tmp | 0x7fff86ae7220 | 0x7fff86ae72C8 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae72C8 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae7240 | | .. | .. | .. | | node_3_value | 0x7fff86ae72C8 | 108 | | node_3_next | 0x7fff86ae72CC | 0x7fff86ae72D0 | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] nodepp[shape=plaintext,fontcolor=blue] tmp[shape=plaintext,fontcolor=orange] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; D [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_4</TD> <TD PORT="val">109</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> C:val; B:next:c -> A:w; C:next:c -> D:val; D:next:c -> Nil:w; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> B; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; nodepp -> A:next; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; tmp:p:s -> C; } } ``` #### 4.(` swap 執行 `) `node = 0x7fff86ae7244`(node_1_next 記憶體位置) `tmp = 0x7fff86ae72C8`(node_3_value 記憶體位置) `*node = (*node)->next;` * `*node` 會是 `node_1_next` 的記憶體位置。 * `(*node)->next `會是 `node_3_next` 所指向的記憶體位置。 所以現在 `node_1_next` 的 value 被改成指向 `node_4`。 `tmp->next = (*node)->next;` * `tmp->next` 所表示的是 `node_3_next`。 * `(*node)->next` 所表示的是 `Nil`。 所以現在 `node_3_next` 的 value 被改成指向 `Nil`。 `(*node)->next = tmp;` 所以現在 `node_4_next` 的 value 被改成指向 `node_3`。 | Name | 記憶體位置 | Value | | ------------ | -------------- | -------------- | | head | 0x7fff86ae7208 | 0x7fff86ae7280 | | .. | .. | .. | | nodepp | 0x7fff86ae721C | 0x7fff86ae7244 | | .. | .. | .. | | tmp | 0x7fff86ae7220 | 0x7fff86ae72C8 | | .. | .. | .. | | node_1_value | 0x7fff86ae7240 | 72 | | node_1_next | 0x7fff86ae7244 | 0x7fff86ae72D0 | | .. | .. | .. | | node_2_value | 0x7fff86ae7280 | 101 | | .. | .. | .. | | node_2_next | 0x7fff86ae72C0 | 0x7fff86ae7240 | | .. | .. | .. | | node_3_value | 0x7fff86ae72C8 | 108 | | node_3_next | 0x7fff86ae72CC | Nil | | node_4_value | 0x7fff86ae72D0 | 109 | | node_4_next | 0x7fff86ae72D4 | 0x7fff86ae72C8 | ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head[shape=plaintext,fontcolor=red] nodepp[shape=plaintext,fontcolor=blue] tmp[shape=plaintext,fontcolor=orange] A [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="">node_1</TD> <TD PORT="val">72</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; B [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_2</TD> <TD PORT="val">101</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; C [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_3</TD> <TD PORT="val">108</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; D [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node_4</TD> <TD PORT="val">109</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; Nil [shape=plaintext]; A:next:c -> D:val; B:next:c -> A:w; C:next:c -> Nil:w; D:next:c -> C:val; { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; head:p:s -> B; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; nodepp -> A:next; } { node [shape=record]; edge [arrowhead=vee,headclip=true, tailclip=true] rank=same; tmp:p:s -> C; } } ``` 不覺得那個 node 指標很熟悉嗎? 沒錯它就是 pointer to pointer,所以 swap_pair() 也可以用 pointer to pointer 來實現。 ### swap_pair() 的 pointer to pointer 版本 ```c= void swap_pair(node_t **head) { node_t *tmp = NULL; for(; *head && (*head)->next; head = &(*head)->next->next ){ tmp = *head; *head = (*head)->next; tmp->next = (*head)->next; (*head)->next = tmp; } } ``` ## reverse() ```c node_t *reverse(node_t *head) { node_t *cursor = NULL; while (head) { node_t *next = head->next; CCC; head = next; } return cursor; } ``` ### `CCC` - [ ] `(a)` `cursor = head; head->next = cursor` - [x] `(b)` `head->next = cursor; cursor = head` - [ ] `(c)` `cursor = head` - [ ] `(d)` `head->next = cursor` - [ ] `(e)` `head->next->next = cursor; cursor->next = head` - [ ] `(f)` `cursor->next = head; head->next->next = cursor` ### 解題思路 先思考如果只有兩個指標這件事情可以嗎? 1. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">head</TD></TR> </TABLE>>]; a [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node1 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; b [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node2</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; c [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node3</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; d [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node4 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; next [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">next</TD></TR> </TABLE>>]; a:next:c -> b:val ; b:next:c -> c:val; c:next:c -> d:val; { rank=same; head:p:s -> a:val; } { rank=same; next:p:s -> b:val; } } ``` 當我們做完 `node1` & `node2`的反轉 2. ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">head</TD></TR> </TABLE>>]; a [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="next"> </TD> <TD PORT="val"> node1 </TD> </TR> </TABLE>>]; b [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="next"> </TD> <TD PORT="val">node2</TD> </TR> </TABLE>>]; c [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node3</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; d [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node4 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; next [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">next</TD></TR> </TABLE>>]; null [label=< <TABLE BORDER="0" CELLBORDER="0" cellspacing="0"> <TR><TD PORT="p">NULL</TD></TR> </TABLE>>]; null:p:e -> a:next:c [dir=back]; a:next:c -> b:val [style=invis]; a:e -> b:next:c [dir=back]; b:next:c -> c:val [style=invis]; c:next:c -> d:val; { rank=same; head:p:s -> a:val; } { rank=same; next:p:s -> b:val; } } ``` 做到這裡我們可以發現我們遺失了 node3 的實體位址,所以這樣是不行的! 1. 所以我們需要加入 `cursor` 來 keep 住前面的點,並且利用 `next` 來防止下一個要做反轉的 `node` 遺失位址。 ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">head</TD></TR> </TABLE>>]; a [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node1 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; cursor [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">cursor</TD></TR> </TABLE>>]; null [label=< <TABLE BORDER="0" CELLBORDER="0" cellspacing="0"> <TR><TD PORT="p">NULL</TD></TR> </TABLE>>]; b [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node2</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; c [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node3</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; d [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node4 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; next [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">next</TD></TR> </TABLE>>]; a:next:c -> b:val ; b:next:c -> c:val; c:next:c -> d:val; { rank=same; cursor:p:s -> null:n; } { rank=same; head:p:s -> a:val; } { rank=same; next:p:s -> b:val; } } ``` 2. 這邊顯示了做了一次後指標所指向的位址,可以看到 cursor 把持住了 node1 位址可以拿來防止遺失,所以我們才會做 `cursor = head ` 來 keep 住每次做反轉 next 的 node,並且由 `head = next;` 來去指向下一個要做的點。 ```graphviz digraph { rankdir=LR; node [shape=none]; edge [arrowhead=vee,headclip=false, tailclip=false] head [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">head</TD></TR> </TABLE>>]; a [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="next"> </TD> <TD PORT="val"> node1 </TD> </TR> </TABLE>>]; cursor [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">cursor</TD></TR> </TABLE>>]; null [label=< <TABLE BORDER="0" CELLBORDER="0" cellspacing="0"> <TR><TD PORT="p">NULL</TD></TR> </TABLE>>]; b [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node2</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; c [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val">node3</TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; d [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR> <TD PORT="val"> node4 </TD> <TD PORT="next"> </TD> </TR> </TABLE>>]; next [label=< <TABLE BORDER="0" CELLBORDER="1" cellspacing="0"> <TR><TD PORT="p">next</TD></TR> </TABLE>>]; null:p:e -> a:next:c [dir=back]; a:next:c -> b:val [style=invis]; b:next:c -> c:val; c:next:c -> d:val; { rank=same; cursor:p:s -> a:val; } { rank=same; head:p:s -> b:val; } { rank=same; next:p:s -> b:val; } } ```