LeetCode146.LRU緩存機制
題目
思路分析
要實現本體的兩種操作,需要用到一個哈希表和一個雙向鏈表。
方法:哈希表+雙向鏈表
LRU緩存機制可以通過哈希表輔以雙向鏈表實現。首先先用一個哈希表和一個雙向鏈表維護所有在緩存中的鍵值對
- 雙向鏈表按照被使用的順序存儲這些鍵值對,靠近頭部的是最近使用的
- 哈希表,通過魂村數據的鍵值對映射到其在雙向鏈表中的位置
首先使用哈希表進行定位,找出緩存在雙向鏈表中的位置,然後將其移動到頭部。
- 對於get操作,首先判斷key是否存在
- 如果不存在返回-1
- 如果存在,通過哈希表定位到該節點在雙向鏈表中的位置,然後將其移動到雙線鏈表的頭部,並返回該節點的值
- 對於put操作,先判斷key是否存在
- 如果key不存在,使用key和value創建一個新的節點,在雙向鏈表的頭部添加該節點,並將key和該節點添加進哈希表,然後判斷雙向鏈表是否超出容量
- 如果key存在,則與get操作類似,先通過哈希表定位,再將對應的節點的值更新爲value,並將該節點移到雙線鏈表的頭部
在雙線鏈表的實現中,使用一個僞頭部和僞尾部標記界限,這樣在添加節點和刪除節點的時候就不需要檢查相鄰的節點是否存在。
代碼實現
public class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 使用僞頭部和僞尾部節點
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
// 如果 key 存在,先通過哈希表定位,再移到頭部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
// 如果 key 不存在,創建一個新的節點
DLinkedNode newNode = new DLinkedNode(key, value);
// 添加進哈希表
cache.put(key, newNode);
// 添加至雙向鏈表的頭部
addToHead(newNode);
++size;
if (size > capacity) {
// 如果超出容量,刪除雙向鏈表的尾部節點
DLinkedNode tail = removeTail();
// 刪除哈希表中對應的項
cache.remove(tail.key);
--size;
}
}
else {
// 如果 key 存在,先通過哈希表定位,再修改 value,並移到頭部
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}