1. 程式人生 > >Chapter 6 連結串列(上):如何實現LRU快取淘汰演算法?

Chapter 6 連結串列(上):如何實現LRU快取淘汰演算法?

快取淘汰策略:

 

一、什麼是連結串列?

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)首節點的前驅指標prev和尾節點的後繼指標均指向空地址。

3)效能特點:

和單鏈表相比,儲存相同的資料,需要消耗更多的儲存空間。

插入、刪除操作比單鏈表效率更高O(1)級別。以刪除操作為例,刪除操作分為2種情況:給定資料值刪除對應節點和給定節點地址刪除節點。對於前一種情況,單鏈表和雙向連結串列都需要從頭到尾進行遍歷從而找到對應節點進行刪除,時間複雜度為O(n)。對於第二種情況,要進行刪除操作必須找到前驅節點,單鏈表需要從頭到尾進行遍歷直到p->next = q,時間複雜度為O(n),而雙向連結串列可以直接找到前驅節點,時間複雜度為O(1)。

對於一個有序連結串列,雙向連結串列的按值查詢效率要比單鏈表高一些。因為我們可以記錄上次查詢的位置p,每一次查詢時,根據要查詢的值與p的大小關係,決定是往前還是往後查詢,所以平均只需要查詢一半的資料。

4.雙向迴圈連結串列:首節點的前驅指標指向尾節點,尾節點的後繼指標指向首節點。

四、選擇陣列還是連結串列?

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

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

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

2.陣列缺點

1)若申請記憶體空間很大,比如100M,但若記憶體空間沒有100M的連續空間時,則會申請失敗,儘管記憶體可用空間超過100M。

2)大小固定,若儲存空間不足,需進行擴容,一旦擴容就要進行資料複製,而這時非常費時的。

3.連結串列缺點

1)記憶體空間消耗更大,因為需要額外的空間儲存指標資訊。

2)對連結串列進行頻繁的插入和刪除操作,會導致頻繁的記憶體申請和釋放,容易造成記憶體碎片,如果是Java語言,還可能會造成頻繁的GC(自動垃圾回收器)操作。

4.如何選擇?

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

如果程式碼對記憶體的使用非常苛刻,那陣列就更適合。