資結筆記|鏈結串列(linked list)

更新 發佈閱讀 8 分鐘

基本觀念


  鏈結串列(linked list)表面上也是一串數列,但與陣列不同的地方是,陣列的資料元素是在記憶體的連續空間,鏈結串列的資料是散落在記憶體的各處。而我們會將存放資料的地方稱為節點,每個節點包含2個區塊,一個區塊是資料區用來存放數據,另一個區塊是指標區用來指向下一個節點元素。

  下列是一個鏈結串列含有3個節點,要存放的內容為dog、cat、fish,我們來看看這個鏈結串列的外觀,如下圖,

raw-image

而實際上,因為鏈結串列是分散在記憶體的各處,所以有可能不是連續排列的,如下圖。但透過指標,可以將分散在各處的資料連結在一起。


raw-image

資料讀取


  鏈結串列讀取資料使用的是循序讀取(sequential access),必須得從頭開始讀取資料。以上面的鏈結陣列舉例,假設要讀取FISH,得先經過DOG->CAT->FISH,按照順序才能取得FISH的資料,因為要跑完整個串列,所以時間複雜度為O(n)。


新增&刪除節點元素


  在新增節點時,不需要搬動整個串列的資料,只需要將前一個元素的指標指向新元素,並將新元素的指標指向原本的下一個元素。例如我要將APPLE插入CAT和FISH中間,只要先將CAT指向APPLE(CAT->APPLE),然後再將APPLE指向FISH(APPL->FISH),即可形成DOG->CAT->APPLE->FISH,新的鏈結串列。

  刪除節點也是同樣的道理,只要將欲刪除的節點的前一個節點的指標,指向下一個節點,讓這個鏈結串列無法走到想刪除的節點,就算完成刪除節點的動作了。


循環鏈結串列


  在鏈結串列中有頭尾的觀念,搜尋時必須從頭至尾,但如果我們將最尾端的節點指向第一個節點,使它成為一個循環,就稱為循環鏈結串列。見下圖,這樣設計的好處是不管目前指標示指向哪一個節點,都可以搜尋整個串列,不會因為指到到尾巴而中止。

raw-image

陣列與鏈結串列時間複雜度比較


raw-image

由上述表格可知,不同的資料結構有各自的優缺點,會影響不同操作的時間複雜度,因此未來在設計程式時,可以根據需求決定要使用哪一種資料結構。



鏈結串列程式實作


  推薦大家一個實作程式碼可以使用的工具python tutor,不用特別安裝可以及時修改,還可以看到視覺化的資料結構,推薦初學者使用。


建立鏈結串列

class Node():
def __init__ (self, data=None):
self.data = data #資料data
self.next = None #指標pointer


p1 = Node(7) #節點1
p2 = Node(9) #節點2
p3 = Node(11) #節點3

p1.next = p2 #節點1指向節點2
p2.next = p3 #節點2指向節點3

# 設定頭節點
head = p1

# 輸出鏈結串列的內容
ptr = head
while ptr:
print(ptr.data, end=" -> ")
ptr = ptr.next
print("None") #移動指標到下一個節點


可以從上面的程式碼看到Node類別有兩個屬性,data是用來儲存節點的資料,next是用來儲存指標,指標會指向下一個節點。第一個節點為頭節點用head設定,如果陣列結束了就指向None,執行結果如下圖,

raw-image


插入新節點在開頭


  假設我要將一筆資料5插入這個鏈結串列成為新的頭節點,只需要新增一個節點(p0)並指向原本的頭節點(p1),並將新建的節點(p0)指定為新的頭節點。


class Node:
def __init__(self, data=None):
self.data = data # 節點儲存的資料
self.next = None # 下一個節點的指標(預設為 None)

# 創建初始的鏈結串列節點
p1 = Node(7) # 節點1
p2 = Node(9) # 節點2
p3 = Node(11) # 節點3

# 串接節點
p1.next = p2 # 節點1指向節點2
p2.next = p3 # 節點2指向節點3

# 新增節點到鏈結串列的最前面
p0 = Node(5) # 新的節點
p0.next = p1 # 新節點指向原本的第一個節點

# 設定新的頭節點
head = p0

# 輸出鏈結串列的內容
ptr = head
while ptr:
print(ptr.data, end=" -> ")
ptr = ptr.next
print("None")

執行結果如下圖,

raw-image


插入新節點在中間


  假設我要再新增一筆資料8在節點p1和p2中間,只需要新增一個節點(p4),將p1指向p4,再將p4指向P2則可以完成。


class Node:
def __init__(self, data=None):
self.data = data # 節點儲存的資料
self.next = None # 下一個節點的指標(預設為 None)

# 創建初始的鏈結串列節點
p0 = Node(5)
p1 = Node(7) # 節點1
p2 = Node(9) # 節點2
p3 = Node(11) # 節點3

# 新增節點在鏈結串列中間
p4 = Node(8) # 節點4

# 串接節點
p0.next = p1
p1.next = p4 # 節點1指向節點4
p2.next = p3
p4.next = p2 # 節點4指向節點2
# 設定頭節點

head = p0

# 輸出鏈結串列的內容

ptr = head

while ptr:

print(ptr.data, end=" -> ")

ptr = ptr.next

print("None")


raw-image


建立循環鏈結串列


  我們要建立一個循環連結只需將p3再指向p0即可,但輸出的時候要加入一個檢查機制,如果回到頭節點了則停止輸出,以避免無窮迴圈。


class Node:
def __init__(self, data=None):
self.data = data # 節點儲存的資料
self.next = None # 下一個節點的指標(預設為 None)

# 創建初始的鏈結串列節點
p0 = Node(5) # 節點0
p1 = Node(7) # 節點1
p2 = Node(9) # 節點2
p3 = Node(11) # 節點3

# 新增節點在鏈結串列中間
p4 = Node(8) # 節點4

# 串接節點
p0.next = p1 # 節點0指向節點1
p1.next = p4 # 節點1指向節點4
p4.next = p2 # 節點4指向節點2
p2.next = p3 # 節點2指向節點3
p3.next = p0 # 節點3指向節點0,形成循環鏈結串列

# 設定頭節點
head = p0

# 輸出循環鏈結串列的內容

ptr = head
while True:
print(ptr.data, end=" -> ")
ptr = ptr.next
if ptr == head: # 如果回到頭節點,停止遍歷
break
print(ptr.data, "(回到頭節點)")

以下是輸出結果,


raw-image


留言
avatar-img
留言分享你的想法!
avatar-img
資治通艦的沙龍
3會員
45內容數
人生中有的時候你會感知到,現在就是那個命運的分歧點,如果我不挽起袖子努力的話,我這一輩子大概就這樣了,所以我決定開始這個部落格,記錄我每天的努力,也希望可以分享學習的筆記與心得,大家可以一起交流學習。
資治通艦的沙龍的其他內容
2025/04/08
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
2025/04/08
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
2025/04/07
第 1 題 題目 設有一三維陣列(three dimensional matrix) A[1...u₁, 1...u₂, 1...u₃],請問其中一元素(element)A[i,j,k] 之儲存位置為何?其中 1 ≤ i ≤ u₁,1 ≤ j ≤ u₂,1 ≤ k ≤ u₃,請列出推導過程。
Thumbnail
2025/04/07
第 1 題 題目 設有一三維陣列(three dimensional matrix) A[1...u₁, 1...u₂, 1...u₃],請問其中一元素(element)A[i,j,k] 之儲存位置為何?其中 1 ≤ i ≤ u₁,1 ≤ j ≤ u₂,1 ≤ k ≤ u₃,請列出推導過程。
Thumbnail
2025/04/07
陣列和鏈結串列的比較以及陣列位址的計算方式
Thumbnail
2025/04/07
陣列和鏈結串列的比較以及陣列位址的計算方式
Thumbnail
看更多
你可能也想看
Thumbnail
不是每個人都適合自己操盤,懂得利用「專業」,才是績效拉開差距的開始
Thumbnail
不是每個人都適合自己操盤,懂得利用「專業」,才是績效拉開差距的開始
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
題目敘述 輸入給定一個鏈結串列的head node。 要求我們進行化簡,只要某個節點的右手邊存在比較大的節點,就刪除掉。 例如 5->2->13->3 5的右手邊有13,所以5刪除掉。 2的右手邊有13,所以2刪除掉。 13的右手邊沒有更大的節點,所以13留著。 3的右手邊沒有更大
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News