# Singly Linked-list ###### tags: `tutorial` [TOC] ## 簡介 Linked-list 是一種資料結構,利用指標把資料像火車一樣一節一節的連結在一起 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 12 | <ref> }", width=1.2] b [label="{ <data> 99 | <ref> }"]; c [label="{ <data> 37 | <ref> }"]; null [shape=box]; a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2]; b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> null [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; head -> a } ``` 每一節"車廂"我們叫作 node,可以根據想儲存的資料型式決定中間要有哪些資料,不過一定要有指標指到下一節的位置 因此 struct 可以宣告成這樣 ```cpp= struct Node { int data; // 這邊用 int 當範例,實際上不一定只能有一個 int Node* next; }; ``` 這邊儲存一個 int 當作資料,用 next 當成指到下個資料的指標 ## 建立 Linked list ### 概念 一開始我們要有一個指標,保存第1節的位置 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; head -> NULL } ``` 接下來宣告另一個指標做操作,並產生新的一節,把資料填進去 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] head -> NULL cur -> a a:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把 head 指到第1節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] head -> a cur -> a a:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 接下來要產生第 2 節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> a cur -> b a:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把第1節的 next 指到第 2 節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> a cur -> b a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 第 3 節之後都是按照第 2 節的方法處理 ### 程式碼實作 直接寫死 ```cpp= Node *head = NULL; Node *cur = new Node(); cur->data = 5; // 這邊可以修改成你要的資料 cur->next = NULL; head = cur; cur = new Node(); cur->data = 6; // 這邊可以修改成你要的資料 cur->next = NULL; head->next = cur; ``` 連續操作 ```cpp= // 第1節 Node *head = new Node(); head->next = NULL; Node *cur = head; for(int i = 0; i < 5; ++i){ // 這邊可以修改成你要的判斷條件 Node newNode = new Node(); newNode->next = NULL; cur->data = i; // 這行可以改成你要插入的資料 cur->next = newNode; cur = cur->next; } ``` ## 刪除 Linked list ### 概念 先用一個 cur 指標指到 head 的那一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> a cur -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把 head 移到下一個 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> b cur -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 釋放 cur ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; b [label="{ <data> 6 | <ref> }", width=1.2] head -> b cur b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 連續做到 head = NULL 為止 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; head -> "NULL" } ``` ### 程式碼實作 ```cpp= while(head != NULL){ Node *cur = head; head = head->next; delete cur; } ``` ## 查詢資料 用一個 cur 指標,從 head 開始一個一個比較,如果不滿足條件就繼續找 next ```cpp= Node *cur = head; while(cur != NULL && cur->data != 6){ // 這邊可以修改成你要的判斷條件 cur = cur->next; } ``` 找到的話 cur 會指到要找的 node,沒找到的話會指到 NULL; ## 插入節點 ### 從頭插入 這是最簡單的插入方法,不過連續插入的話資料的順序會反轉 #### 概念 創建新的一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] c [label="{ <data> 4 | <ref> }", width=1.2] head -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; cur -> c c:ref:c -> "NULL" [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把 cur 那節的 next 指到 head ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] c [label="{ <data> 4 | <ref> }", width=1.2] head -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; cur -> c c:ref:c -> a [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把 head 指到 cur ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] c [label="{ <data> 4 | <ref> }", width=1.2] head -> c a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; cur -> c c:ref:c -> a [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` #### 程式碼實作 ```cpp= // 第1節 Node *cur = new Node(); cur->data = 4; // 這邊可以修改成你要的資料 cur->next = head; head = cur; ``` ### 從中間插入 這邊比較麻煩,要小心不要讓連結斷掉 #### 概念 創建 cur 指標,並且把 cur 移到要插入的位置 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 4 | <ref> }", width=1.2] b [label="{ <data> 5 | <ref> }", width=1.2] c [label="{ <data> 6 | <ref> }", width=1.2] head -> a cur -> b a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 再創建另一個指標,建立新的一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 4 | <ref> }", width=1.2] b [label="{ <data> 5 | <ref> }", width=1.2] c [label="{ <data> 6 | <ref> }", width=1.2] d [label="{ <data> 7 | <ref> }", width=1.2] head -> a cur -> b "newNode" -> d a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 新的 next 指到 cur 的 next ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 4 | <ref> }", width=1.2] b [label="{ <data> 5 | <ref> }", width=1.2] c [label="{ <data> 6 | <ref> }", width=1.2] d [label="{ <data> 7 | <ref> }", width=1.2] head -> a cur -> b "newNode" -> d a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; d:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` cur 的 next 指到新的那節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 4 | <ref> }", width=1.2] b [label="{ <data> 5 | <ref> }", width=1.2] c [label="{ <data> 6 | <ref> }", width=1.2] d [label="{ <data> 7 | <ref> }", width=1.2] head -> a cur -> b "newNode" -> d a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> d [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; d:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` #### 程式碼實作 ```cpp= // 找到要的位置 Node *cur = head; while(cur != NULL && cur->data != 5){ // 這邊可以修改成你要的判斷條件 cur = cur->next; } Node *newNode = new Node(); newNode->data = 7; // 這邊可以修改成你要的資料 newNode->next = cur->next; cur->next = newNode; ``` ### 從尾巴插入 #### 概念 先創建 cur 指標,指到最後一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; cur -> b } ``` 直接用 cur 的 next 產生新的一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] c [label="{ <data> 7 | <ref> }", width=1.2] head -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; cur -> b } ``` #### 程式碼實作 ```cpp= // 找到最尾 Node *cur = head; while(cur->next != NULL){ cur = cur->next; } cur->next = new Node(); cur->next->data = 7; // 這邊可以修改成你要的資料 cur->next->next = NULL; ``` ### 完整插入 通常在用的時候不會知道是頭、中、尾,這是結合頭、中、尾三種方式的插入方法 #### 程式碼實作 ```cpp= // 找到最尾 Node *cur = head; while(cur != NULL && cur->next != NULL && cur->data != 5){ // 這邊可以修改成你要的判斷條件 cur = cur->next; } if(cur == NULL){ // 頭 cur = new Node(); cur->data = 5; // 這邊可以修改成你要的資料 cur->next = head; head = cur; }else if(cur->next == NULL){ // 尾 cur->next = new Node(); cur->next->data = 5; // 這邊可以修改成你要的資料 cur->next->next = NULL; }else{ // 中 Node *newNode = new Node(); newNode->data = 5; // 這邊可以修改成你要的資料 newNode->next = cur->next; cur->next = newNode; } ``` ## 刪除節點 刪除的時候要小心不要讓連結斷掉 ### 刪除第 1 節 #### 概念 :::info 其實就是前面刪除 Linked-List 的方法,只是不連續做而已 ::: 先用一個 cur 指標指到 head 的那一節 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> a cur -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 把 head 移到下一個 ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2] head -> b cur -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 釋放 cur ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; b [label="{ <data> 6 | <ref> }", width=1.2] head -> b cur b:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` ### 程式碼實作 ```cpp= Node *cur = head; head = head->next; delete cur; ``` ### 刪除中間 & 尾巴 :::info 中間 & 尾巴方法一樣 ::: #### 概念 先用一個 cur 指標指到要刪的 **前** 一節 :::warning **前** 一節!!! **前** 一節!!! **前** 一節!!! ::: ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2, color=red] c [label="{ <data> 7 | <ref> }", width=1.2] cur [color=red] head -> a cur -> a a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 創一個新指標,指到要刪掉的 **那** 一節 :::warning **那** 一節!!! **那** 一節!!! **那** 一節!!! ::: ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2, color=red] c [label="{ <data> 7 | <ref> }", width=1.2] del [color=red] head -> a cur -> a del -> b a:ref:c -> b [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` cur 的 next 指到 del 的 next ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] b [label="{ <data> 6 | <ref> }", width=1.2, color=red] c [label="{ <data> 7 | <ref> }", width=1.2] del [color=red] head -> a cur -> a del -> b a:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; b:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` 釋放 del ```graphviz digraph foo { rankdir=LR; node [shape=record]; head [shape=box]; a [label="{ <data> 5 | <ref> }", width=1.2] c [label="{ <data> 7 | <ref> }", width=1.2] del [color=red] head -> a cur -> a a:ref:c -> c [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; c:ref:c -> "NULL " [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false]; } ``` #### 程式碼實作 ```cpp= Node *cur = head; // 找到位置 while(cur->next->next != NULL && cur->next->data != 6){ // 這邊可以修改成你要的判斷條件 cur = cur->next; } Node *del = cur->next; cur->next = del->next; delete del; ``` ### 完整刪除 ```cpp= // 找到最尾 Node *cur = head; while(cur != NULL && cur->next != NULL && cur->data != 6 && cur->next->next != NULL && cur->next->data != 6){ // 這邊可以修改成你要的判斷條件 cur = cur->next; } if(cur != NULL){ // 如果是 NULL 表示是空的 if(cur->next == NULL || cur->data == 6){ // 第 1 節 head = head->next; delete cur; }else{ // 中間或尾巴 Node *del = cur->next; cur->next = del->next; delete del; } } ```