1. 程式人生 > >資料結構與演算法學習筆記之 提高讀取效能的連結串列(上)

資料結構與演算法學習筆記之 提高讀取效能的連結串列(上)

前言

  連結串列(Linked list)比陣列稍微複雜一點,在我們生活中用到最常見的應該是快取,它是一種提高資料讀取效能的技術,常見的如cpu快取,瀏覽器快取,資料庫快取等。今天我們就來學習一下連結串列

正文

一、連結串列的定義?

1.一種線性表(資料排成像一條線一樣的結構。每個線性表上的資料最多有前後兩個方向);
2.從儲存結構來看,通過“指標”,將一組零散的記憶體塊串聯起來使用的資料結構;
3.連結串列中的每一個記憶體塊被稱為結點Node,結點除了儲存資料外,還需記錄鏈上下一個節點的地址(next)

二、連結串列的優缺點

1.插入、刪除資料效率高,時間複雜度為O(1)(只需更改指標指向即可),隨機訪問效率低,時間複雜度為O(n)級別(需要從鏈頭至鏈尾進行遍歷)。
2.和陣列相比,記憶體空間消耗更大,因為每個儲存資料的節點都需要額外的空間儲存後繼指標。


三、常用連結串列:單鏈表、迴圈連結串列、雙向連結串列、雙向迴圈連結串列和塊狀連結串列

1.單鏈表

1)每個節點只包含一個指標,即後繼指標。
2)單鏈表有兩個特殊的節點,即首節點和尾節點。

用首節點地址表示整條連結串列,尾節點的後繼指標指向空地址null。

3)效能特點:插入和刪除節點的時間複雜度為O(1),查詢的時間複雜度為O(n)。


2.迴圈連結串列

1)除了尾節點的後繼指標指向首節點的地址外均與單鏈表一致。
2)適用於儲存有迴圈特點的資料,比如約瑟夫問題。


3.雙向連結串列

1)節點除了儲存資料外,還有兩個指標分別指向前一個節點地址(前驅指標prev)和下一個節點地址(後繼指標next)。
2)當此“連線”為第一個“連線”時,指向空值或者空列表

當此“連線”為最後一個“連線”時,指向空值或者空列表)

3)效能特點:
和單鏈表相比,儲存相同的資料,需要消耗更多的儲存空間。
插入、刪除操作比單鏈表效率更高O(1)級別。

以刪除操作為例,刪除操作分為2種情況:

給定資料值刪除對應節點和給定節點地址刪除節點。

對於前一種情況,單鏈表和雙向連結串列都需要從頭到尾進行遍歷從而找到對應節點進行刪除,時間複雜度為O(n)。

對於第二種情況,要進行刪除操作必須找到前驅節點,單鏈表需要從頭到尾進行遍歷直到p->next = q,時間複雜度為O(n),而雙向連結串列可以直接找到前驅節點,時間複雜度為O(1)。


對於一個有序連結串列,雙向連結串列的按值查詢效率要比單鏈表高一些。

因為我們可以記錄上次查詢的位置p,每一次查詢時,根據要查詢的值與p的大小關係,決定是往前還是往後查詢,所以平均只需要查詢一半的資料。

4.雙向迴圈連結串列(雙向,迴圈連結串列的結合)

首節點的前驅指標指向尾節點,尾節點的後繼指標指向首節點。

5.塊狀連結串列

塊狀連結串列本身是一個連結串列,但是連結串列儲存的並不是一般的資料,而是由這些資料組成的順序表。每一個塊狀連結串列的節點,也就是順序表,可以被叫做一個

塊狀連結串列通過使用可變的順序表的長度和特殊的插入、刪除方式,可以在達到{\displaystyle O({\sqrt {n}})}O({\sqrt  n})的複雜度。塊狀連結串列另一個特點是相對於普通連結串列來說節省記憶體,因為不用儲存指向每一個數據節點的指標。


四、陣列VS連結串列

1.插入、刪除和隨機訪問的時間複雜度

陣列:插入、刪除的時間複雜度是O(n),隨機訪問的時間複雜度是O(1)。
連結串列:插入、刪除的時間複雜度是O(1),隨機訪問的時間複雜端是O(n)。

2.陣列缺點

1)若申請記憶體空間很大,比如100M,但若記憶體空間沒有100M的連續空間時,則會申請失敗,儘管記憶體可用空間超過100M。
2)大小固定,若儲存空間不足,需進行擴容,一旦擴容就要進行資料複製,而這時非常費時的。

3.連結串列缺點

1)記憶體空間消耗更大,因為需要額外的空間儲存指標資訊。
2)對連結串列進行頻繁的插入和刪除操作,會導致頻繁的記憶體申請和釋放,容易造成記憶體碎片,如果是Java語言,還可能會造成頻繁的GC(自動垃圾回收器)操作。

4.如何選擇

陣列簡單易用,在實現上使用連續的記憶體空間,可以藉助CPU的緩衝機制預讀陣列中的資料,所以訪問效率更高,而連結串列在記憶體中並不是連續儲存,所以對CPU快取不友好,沒辦法預讀。
如果程式碼對記憶體的使用非常苛刻,那陣列就更適合

CPU快取機制指的是什麼?為什麼就陣列更好了?

 CPU在從記憶體讀取資料的時候,會先把讀取到的資料載入到CPU的快取中。而CPU每次從記憶體讀取資料並不是只讀取那個特定要訪問的地址,而是讀取一個數據塊(這個大小我不太確定。。)並儲存到CPU快取中,然後下次訪問記憶體資料的時候就會先從CPU快取開始查詢,如果找到就不需要再從記憶體中取。這樣就實現了比記憶體訪問速度更快的機制,也就是CPU快取存在的意義:為了彌補記憶體訪問速度過慢與CPU執行速度快之間的差異而引入。
對於陣列來說,儲存空間是連續的,所以在載入某個下標的時候可以把以後的幾個下標元素也載入到CPU快取這樣執行速度會快於儲存空間不連續的連結串列儲存。

相關文章

以上內容為個人的學習筆記,僅作為學習交流之用。

 

歡迎大家關注公眾號,不定時乾貨,只做有價值的輸出

版權:本文版權歸作者
轉載:歡迎轉載,但未經作者同意,必須保留此段宣告;必須在文章中給出原文連線;否則必究法律責任