三、連結串列(Linked List)(原理)
前言
經典的連結串列應用場景:LRU 快取淘汰演算法
快取是一種提高資料讀取效能的技術,由於快取的大小有限,當快取被用滿時,哪些資料應該被清理出去,哪些資料應該被保留?這就需要快取淘汰策略來決定。
- 常見的策略有三種:
- 先進先出策略 FIFO(First In,First Out)
- 最少使用策略 LFU(Least Frequently Used)
- 最近最少使用策略 LRU(Least Recently Used)
==》如何用連結串列來實現 LRU 快取淘汰策略呢? ==》詳見 五 的解答
一、連結串列
連結串列:不需要一塊連續的記憶體空間,它通過“指標”將一組零散的記憶體塊串聯起來使用.
記憶體分佈:
最常用的連結串列結構:單鏈表、雙向連結串列和迴圈連結串列
二、單鏈表
1、基本概念
結點(Node):單鏈表的結點結構
連結串列中的每一個數據元素稱為 “結點” ,每個結點都由兩部分組成:資料域 + 指標域。其中,資料域儲存資料元素資訊,指標域儲存連結串列的下一個結點的位置資訊,是一個指標。
單鏈表:當一個序列中只含有指向它的後繼結點的連結時,就稱該連結串列為單鏈表。
- 非空表(有頭結點):
- 空表:
頭指標:是指連結串列指向第一個結點的指標,若連結串列有頭結點,則是指向頭結點的指標。頭指標具有標識作用。任何情況下,頭指標都存在
頭結點:為了操作的統一和方便(插入/刪除首元結點)設立的,放在首元結點(第一元素結點)之前,其資料域一般無意義(也可以 存放連結串列的長度)。非必需要素。
最後一個結點:最後一個結點指標為“空”(通常用NULL或“^”符號表示),是連結串列的結束標誌,表示它沒有後繼結點。
2、查詢操作
目標:隨機訪問第 k 個元素 ==》依次遍歷 ==》時間複雜度:O(n)
3、插入操作
插入 x 結點: ==》時間複雜度:O(1)
① x->next = a2 -> next
② a2 -> next = x
4、刪除操作
刪除 a2 結點: ==》時間複雜度:O(1)
① p = a1->next
②
a1->next = p->next
三、迴圈連結串列
迴圈連結串列:與單鏈表的唯一區別就是最後一個結點的指標指向連結串列的頭結點。
優點: 從鏈尾到鏈頭比較方便。當要處理的資料具有環型結構特點時,就特別適合採用迴圈連結串列。eg: 約瑟夫問題
四、雙向連結串列
雙向連結串列:每個結點不止有一個後繼指標 next 指向後面的結點,還有一個前驅指標 prev 指向前面的結點。
特點:支援雙向遍歷,更具靈活性;O(1) 時間複雜度的情況下前驅結點
結點:
2、雙向連結串列的優勢
(1)刪除
刪除的兩種情況:
- 刪除結點中“值等於某個給定值”的結點
- 刪除給定指標指向的結點
第一種情況
無論單鏈表還是雙向連結串列,均需要從頭開始遍歷對比,直至找到值等於給定值的結點,然後再執行刪除操作。==》時間複雜度:O(n)
第二種情況
已找到需刪除的結點,但是刪除某個結點 q 需要得到其前驅結點,雙向連結串列可以不用遍歷就得到前驅結點。 ==》時間複雜度:O(1)
(2)有序連結串列的查詢操作
對於有序連結串列,雙向連結串列的按值查詢的效率要比單鏈表高一些。具體來說,可以記錄上次查詢的位置 p ,每次查詢時,根據查詢值與 p 的大小關係,決定向前還是向後查詢。
五、設計思想:空間 <-> 時間
- 當記憶體足夠時,若追求程式碼的執行速度 ==》選擇空間複雜度高、時間複雜度相對較低的演算法或者資料結構
- 當記憶體比較緊張 ==》時間換空間
連結串列實現LRU快取淘汰演算法:越靠近連結串列尾部的結點是越早之前訪問的
- 若此資料之前就在快取連結串列中,遍歷得到該資料所對應的結點,並將其從原來的位置刪除,然後插入到連結串列的頭部。
- 若此資料沒在快取連結串列中,具體分為以下兩種情況:
- 若此時快取未滿 ==》此結點直接插入到連結串列的頭部;
- 若此時快取已滿,則連結串列的最後一個結點刪除,並將新的資料結點插入連結串列的頭部