程式碼範例參考:NTUCPC Guide,此筆記僅為個人學習用途。
一般的陣列會是在連續的記憶體空間中儲存資料,而鏈結串列雖與陣列同為線性資料結構,但他可以將資料儲存在一個 不連續(non-contiguous) 的記憶體空間中。
linked-list 的定義為數個節點(nodes)的集合,而一個節點就由兩個成員所組成,也就是值跟前後的指標(value and previous / next pointer)。
linked-list 可以有三種型態:
那首先最簡單的當然就是單向的 linked-list 了,要如何簡單的理解 linked-list 呢?就如同上課傳紙條一樣,假設有一排座位,某個同學要傳紙條到最前面的同學,勢必要點他前面同學的肩膀叫他接力下去,就有點像下圖那樣:
圖:作者繪製
Head 就是這個鏈結串列的開頭,A 同學經過一連串的同學接力傳紙條後,直到傳給 F 同學為止,而最終鏈結串列結束時要指向空指標 NULL,此時我們可以假設講台是 NULL。
那在這時候(「單向」的鏈結串列)只能朝向一個方向,也就是 Next,並不能往前(Previous)。
而雙向鏈結串列就有點像是點同學肩膀然後那個同學回頭一樣,多了一個往前(Previous)的動作,像是往回看點人的同學,如下圖:
由於雙向鏈結串列多了 Prev 的動作,所以 A 作為開頭也能做 Prev 的動作,只不過 Prev 後就沒東西了,所以是 NULL。
最後是循環鏈結串列,其實他跟單向鏈結串列差不多,只是跑到最後一個節點會回到第一個節點去。
一樣是舉傳紙條的例子,傳到最後一個人的時候,F 同學可能看到某些勁爆的內容,很生氣地把紙條丟回去給 A 同學 XD。
可用 OOP 實作。
初始化一個節點(Node),包含 data、pointer。
基本操作:
把新節點的 next 指向原本的 head,再更新 head 就行。
要先遍歷完整個鏈結串列,直到最後一個節點,把 next 指向新節點;若串列為空,則直接更新 head 就好。
直接將 head 指向 head->next,並釋放原本的頭節點記憶體。
遍歷由 head 開始,依序存取每個節點的 data,直到節點指標為 nullptr 為止。
最後我們把它合起來變成一個完整的程式碼,如下:
註:此程式的鏈結串列是以 1 作為起始索引。
至於在指定位置插入節點的方式,可結合前兩種方式(在串列頭插入節點、在串列尾插入節點):
先遍歷至目標的前一個位置,再調整指標串接新節點。若位置(索引)為 1 則與串列頭插入的方式相同;若超出串列長度則將插入方式改為在串列尾插入節點。
而於指定位置刪除節點的方式:
若位置為 1,與刪除串列頭節點相同;否則遍歷至目標前一個節點,調整其 next 指向目標節點的下一節點,並刪除目標節點記憶體。
C++ 的 STL 函式庫中也提供了 linked-list 的容器,在 STL 中是一個雙向鏈結串列的實作容器。
使用前需要引入標頭檔 #include <list>
。
相關操作如下:
push_back()
:將元素增加到尾端。push_front()
:將元素增加到頭端。pop_back()
:將元素從尾端移除。pop_front()
:將元素從頭端移除。insert()
:插入元素。erase()
:移除某個位置的元素,也可以移除某段範圍的元素。clear()
:清空容器內的所有元素。empty()
:回傳容器是否為空。size()
:回傳容器長度。front()
:存取頭端元素。back()
:存取尾端元素。操作類型 | Array(陣列) | Linked List(鏈結串列) |
---|---|---|
存取(Access) | O(1) | O(n) |
搜尋(Search) | O(n) | O(n) |
插入(Insert) | ||
- 開頭 | O(n)(需移動元素) | O(1) |
- 中間 | O(n)(需移動元素) | O(n)(需遍歷) |
- 尾端 | O(1)(若空間足夠) | O(1)(在 tail 指標下) |
刪除(Delete) | ||
- 開頭 | O(n)(需移動元素) | O(1) |
- 中間 | O(n)(需移動元素) | O(n)(需遍歷) |
- 尾端 | O(1)(若空間足夠) | O(n)(單向需遍歷) |
大小調整 | O(n)(需重新配置) | O(1) |
若為 Doubly Linked List(雙向鏈結串列),尾端刪除可達 O(1),但需額外空間儲存前向指標。
b938. kevin 愛殺殺:https://zerojudge.tw/ShowProblem?problemid=b938