LRU算法(最近最少使用算法)是一種非常常用的頁面置換算法,也可以用于緩存淘汰策略。它的基本思想是根據數據的歷史訪問記錄來進行緩存數據的淘汰。
舉一個例子。假設我們有一個緩存,里面最多可以存儲10個元素。現在我們有一組訪問序列,其中每個元素訪問的時間戳為訪問序列中的位置。這個序列表示的是這10個元素的訪問情況。我們使用LRU算法來進行緩存淘汰。假設初始時緩存中為空,那么緩存中應該存儲的元素是:
a b a c b a d c b a a d c b e a d c b f e a d c g f e a d c g f e a d c g f e
這里我們假設每個元素的大小都相同,可以看到,LRU算法在第4次訪問時淘汰了最早訪問(最不常用)的元素e,這樣一直重復下去。
下面我們來看看如何實現LRU算法。我們可以使用哈希表和雙向鏈表的組合來實現它。哈希表用于存儲緩存元素的key-value對,雙向鏈表用于存儲緩存元素的順序。
class LRUCache { private $capacity; private $map; // hash table to store cache data private $head; // double linked list header private $tail; // double linked list tail public function __construct($capacity) { $this->capacity = $capacity; $this->map = array(); $this->head = null; $this->tail = null; } public function get($key) { if (!isset($this->map[$key])) { return -1; } $node = $this->map[$key]; $this->moveToHead($node); return $node->val; } public function put($key, $value) { if (isset($this->map[$key])) { $node = $this->map[$key]; $node->val = $value; $this->moveToHead($node); return; } $node = new Node($key, $value); $this->map[$key] = $node; $this->addNode($node); if (count($this->map) >$this->capacity) { $lastNode = $this->popTail(); // remove the last node unset($this->map[$lastNode->key]); } } private function addNode($node) { $node->next = $this->head; $node->prev = null; if ($this->head != null) { $this->head->prev = $node; } $this->head = $node; if ($this->tail == null) { $this->tail = $node; } } private function removeNode($node) { if ($node->prev) { $node->prev->next = $node->next; } else { $this->head = $node->next; } if ($node->next) { $node->next->prev = $node->prev; } else { $this->tail = $node->prev; } } private function moveToHead($node) { $this->removeNode($node); $this->addNode($node); } private function popTail() { $tailNode = $this->tail; $this->removeNode($tailNode); return $tailNode; } } class Node { public $key; public $val; public $prev; public $next; public function __construct($key, $val) { $this->key = $key; $this->val = $val; $this->prev = null; $this->next = null; } }
上面的代碼中,addNode方法用于在雙向鏈表頭部添加一個新的節點,removeNode方法用于從雙向鏈表中刪除一個節點,moveToHead方法用于將某一個節點移動到鏈表頭部,popTail方法用于從鏈表尾部彈出一個節點。注意在addNode和removeNode方法中都要同時修改頭指針和尾指針。
上面的實現中,我們使用了一個Node類來表示雙向鏈表中的節點。哈希表中存儲的是key到Node的映射。在get方法中,通過key找到對應的Node并將其移到鏈表頭部。在put方法中,如果key已經存在于哈希表中,我們只需要更新Node的val值并將其移到鏈表頭部即可;否則我們需要新建一個Node并將其加入哈希表和雙向鏈表中。當超過緩存容量時,我們需要刪除鏈表尾部的節點。
總結一下,使用LRU算法可以有效地淘汰不常用的緩存數據,從而提高系統的性能。我們可以使用哈希表和雙向鏈表的組合來實現LRU算法。在實現過程中,需要注意修改頭指針和尾指針以及保持鏈表的正確性。