資料結構 - 連結串列

fatedeity發表於2022-02-11

簡介

連結串列結構

連結串列是一種和陣列不同的線性表結構,陣列的儲存使用了一組連續的記憶體空間,而連結串列通過連結的方式將零散的記憶體空間串聯起來使用。

連結串列的定義需要注意兩點:連結串列仍然是一個線性表結構;連結串列不使用連續的記憶體空間進行儲存。

因此,連結串列克服了陣列需要預先知道資料大小的缺點,並且能充分利用計算機記憶體空間,實現靈活的記憶體動態管理。

但連結串列也失去了快速隨機存取的優點,同時由於增加了結點的指標域,空間開銷較大。

基本概念

連結串列是通過指標將零散的記憶體塊串聯在一起的,這裡的記憶體塊被稱為連結串列的結點

結點除了儲存資料之外,還會儲存下一個結點的地址,這個記錄下一個結點地址的指標被稱為後繼指標。在雙向連結串列中,還會儲存上一個結點的地址,這個記錄上一個結點地址的指標被稱為前驅指標

連結串列中存在兩個比較特殊的結點,分別是第一個結點和最後一個結點。因此也給這兩個結點取了名稱,第一個結點被稱為頭結點,最後一個結點被稱為尾結點

隨機儲存

把連結串列和陣列作比較,陣列具有快速隨機存取的優點,而連結串列是隨機存取效率非常低的資料結構。

如果通過下標去訪問連結串列中的結點,是不能使用定址公式的,只能通過頭結點作為入口,根據指標一個結點一個結點地依次遍歷,直到找到對應的結點。

綜合計算下來,連結串列做隨機訪問的時間複雜度為 \(O(n)\),效率比陣列低得多。

插入、刪除

雖然連結串列沒有了陣列隨機存取的優點,但在插入、刪除結點的時候,效率比陣列高很多。

連結串列的插入和刪除

假設,要在連結串列中插入一個結點,在知道插入位置的前後兩個相鄰結點的前提下,只需將新結點的後繼指標指向下一個結點,然後將上一個結點的後繼指標指向這個新結點,即可完成插入結點的操作。刪除結點也是類似的操作,非常方便。

但是,插入、刪除結點時效率高的前提是知道操作位置的相鄰結點,否則仍需要從頭結點開始尋找到對應位置,這樣的效率會非常低。

五花八門的連結串列

連結串列有很多不同的型別,在上面說的都是最簡單的單向連結串列,複雜一點的還有雙向連結串列、迴圈連結串列。

單向連結串列

單向連結串列是最簡單的連結串列結構,它包含兩個域,一個資訊域和一個指標域。

資訊域儲存實際的資料,指標域儲存此結點的下一個結點位置。

在實際編碼中,為了方便,頭結點的資訊域是空的,其指標域儲存實際的第一個結點所在位置,尾結點的指標域一般會是 NULL 地址。

雙向連結串列

雙向連結串列是在單向連結串列的基礎上多增加了一個指標域,這個新增加的指標域會儲存上一個結點所在位置。

也就是說,在雙向連結串列中,除頭結點的任意結點都可以訪問到上一個結點,因此稱為雙向連結串列。

迴圈連結串列

迴圈連結串列是一個頭結點和尾結點連線在一起的特殊連結串列,通過單向連結串列或雙向連結串列都能夠實現。

迴圈連結串列的優點就是從連結串列的尾結點到頭結點非常方便,也方便處理具有環形結構特點的資料,如約瑟夫問題。

使用上的問題

陣列和連結串列如何選擇

僅特性和效率而言,陣列擁有快速隨機訪問的特性,連結串列可以快速插入、刪除。經常利用下標訪問元素可以使用陣列,經常插入、刪除元素可以使用連結串列。

但是,不能僅僅只用複雜度分析決定使用哪種資料結構。陣列簡單易用,而且能夠藉助 CPU 快取機制預讀陣列中的資料;而連結串列在記憶體中不是連續儲存的,對 CPU 快取不友好,沒辦法有效預讀。

陣列的缺點是資料大小固定,而且一經宣告要佔用整塊連續記憶體空間,如果宣告的陣列過大,系統可能沒有足夠的連續記憶體空間分配給它。即使是在 Java 中使用可以動態擴容的 ArrayList 型別,也存在擴容耗時的問題。而連結串列本身沒有大小的限制,天生支援動態擴容。

實現連結串列的技巧

編寫一個正確的連結串列是比較難的,但是其中也有一些技巧:

  • 理解指標或引用的含義。將某個變數賦值給指標,實際上就是將這個變數的地址賦值給指標,或者反過來說,指標中儲存了這個變數的記憶體地址,這個記憶體地址儲存這個變數,通過指標能找到這個變數
  • 警惕指標丟失。插入結點時,先將插入結點的後繼指標指向下一個結點,再把前一個結點的指標指向插入結點,這樣才不會丟失指標
  • 避免記憶體洩漏。刪除結點時,要記得手動釋放記憶體空間
  • 利用哨兵簡化實現難度。在實際開發當中,如果向空連結串列中插入第一個結點的時候,還需要判斷連結串列中是否已經存在頭結點,嵌入程式碼比較嚴重;但是,如果增加一個哨兵結點,哨兵結點的後繼指標指向頭結點,則可以省略這一步操作
  • 重點留意邊界條件處理。當連結串列為空的時候,程式碼是否能正常工作?當連結串列只有一個結點的時候,程式碼是否能正常工作等等
  • 舉例畫圖,輔助思考。連結串列的指標指向會比較複雜,這種情況可以通過舉例畫圖的辦法將各種情況列舉出來,這樣思路會更加清晰
  • 多寫多練,熟能生巧。寫連結串列程式碼是非常考驗邏輯思維能力的,多多嘗試練習可以提高邏輯思維能力

相關文章