[LeetCode] 146. LRU Cache 近期最少使用缓存
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and set
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.set(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
设计一个近期最少使用页面置换缓存LRU(Least Recently Used),实现get(key), set(key, value)功能。
get(key):取值(key恒为正), 不存在时返回-1。如果存在,返回值,并且delete此key,在从新写入cache,因为要最近刚使用过,要把它放到队尾。
set(key, value):缓存已满,删除近期最久未被使用的节点,添加新节点进缓存。缓存未满,节点存在,修改value;节点不存在,添加新节点进缓存;最后更新此节点到队尾。
解法1: 双向链表(Doubly-Linked List) + HashMap
双向链表:维护缓存节点CacheNode,凡是被访问(新建/修改命中/访问命中)过的节点,一律在访问完成后移动到双向链表尾部,保证链表尾部始终为最新节点;保证链表头部始终为最旧节点,LRU策略删除时表现为删除双向链表头部;由于链表不支持随机访问,使用HashMap+双向链表实现LRU缓存,HashMap中键值对:<key, CacheNode>。
解法2: OrderedDict有序字典
Time: Get O(1) Set O(1), Space: O(N)
Java:
import java.util.HashMap; class Solution { private HashMap<Integer, CacheNode> map; private int capacity; // head.next和tail.next指向链表头尾,包起来防止null private CacheNode head = new CacheNode(-1, -1); private CacheNode tail = new CacheNode(-1, -1); private class CacheNode { int key, value; CacheNode pre, next; CacheNode(int key, int value) { this.key = key; this.value = value; this.pre = null; this.next = null; } } public Solution(int capacity) { this.map = new HashMap<>(); this.capacity = capacity; } // 将已有节点或新建节点移动到链表尾部 private void moveToTail(CacheNode target, boolean isNew) { // 尾部节点显然不需要移动 if (target != tail.next) { if (!isNew) { // 修改旧节点的双向链表指针 target.pre.next = target.next; target.next.pre = target.pre; } // 添加节点到链表尾部 tail.next.next = target; target.pre = tail.next; tail.next = target; } } // 命中节点添加到链表尾部,未命中返回-1 public int get(int key) { if (map.containsKey(key)) { CacheNode target = map.get(key); // 将已有节点移动到链表尾部 moveToTail(target, false); // 此时链表尾部tail.next = target,更新next指向null,防止出现环 tail.next.next = null; return target.value; } return -1; } public void set(int key, int value) { if (map.containsKey(key)) { CacheNode target = map.get(key); target.value = value; map.put(key, target); // 将访问过的已有节点移动到链表尾部 moveToTail(target, false); } else if(map.size() < capacity) { // cache未满,添加节点 CacheNode newNode = new CacheNode(key, value); map.put(key, newNode); if (head.next == null) { head.next = newNode; newNode.pre = head; tail.next = newNode; } else { // 将新建节点移动到链表尾部 moveToTail(newNode, true); } } else { // cache已满,淘汰链表链表头部节点,新节点加入到链表尾部 CacheNode newNode = new CacheNode(key, value); map.remove(head.next.key); map.put(key, newNode); // cache中只有一个元素 if (head.next == tail.next) { head.next = newNode; tail.next = newNode; } else { // cache中不止一个元素,删除头部 head.next.next.pre = head; // 更新新头部.pre = head head.next = head.next.next;// 更新新头部 // 将新建节点移动到链表尾部 moveToTail(newNode, true); } } } }
Python:
class Node: def __init__(self, key, val): self.key = key self.val = val self.prev = None self.next = None class LRUCache: # @param capacity, an integer def __init__(self, capacity): self.capacity = capacity self.size = 0 self.dummyNode = Node(-1, -1) self.tail = self.dummyNode self.entryFinder = {} # @return an integer def get(self, key): entry = self.entryFinder.get(key) if entry is None: return -1 else: self.renew(entry) return entry.val # @param key, an integer # @param value, an integer # @return nothing def set(self, key, value): entry = self.entryFinder.get(key) if entry is None: entry = Node(key, value) self.entryFinder[key] = entry self.tail.next = entry entry.prev = self.tail self.tail = entry if self.size < self.capacity: self.size += 1 else: headNode = self.dummyNode.next if headNode is not None: self.dummyNode.next = headNode.next headNode.next.prev = self.dummyNode del self.entryFinder[headNode.key] else: entry.val = value self.renew(entry) def renew(self, entry): if self.tail != entry: prevNode = entry.prev nextNode = entry.next prevNode.next = nextNode nextNode.prev = prevNode entry.next = None self.tail.next = entry entry.prev = self.tail self.tail = entry
Python:
class ListNode(object): def __init__(self, key, val): self.val = val self.key = key self.next = None self.prev = None class LinkedList(object): def __init__(self): self.head = None self.tail = None def insert(self, node): node.next, node.prev = None, None # avoid dirty node if self.head is None: self.head = node else: self.tail.next = node node.prev = self.tail self.tail = node def delete(self, node): if node.prev: node.prev.next = node.next else: self.head = node.next if node.next: node.next.prev = node.prev else: self.tail = node.prev node.next, node.prev = None, None # make node clean class LRUCache(object): def __init__(self, capacity): self.list = LinkedList() self.dict = {} self.capacity = capacity def _insert(self, key, val): node = ListNode(key, val) self.list.insert(node) self.dict[key] = node def get(self, key): if key in self.dict: val = self.dict[key].val self.list.delete(self.dict[key]) self._insert(key, val) return val return -1 def set(self, key, val): if key in self.dict: self.list.delete(self.dict[key]) elif len(self.dict) == self.capacity: del self.dict[self.list.head.key] self.list.delete(self.list.head) self._insert(key, val)
Python:
class LRUCache: def __init__(self, capacity): self.capacity = capacity self.cache = collections.OrderedDict() def get(self, key): if not key in self.cache: return -1 value = self.cache.pop(key) self.cache[key] = value return value def set(self, key, value): if key in self.cache: self.cache.pop(key) elif len(self.cache) == self.capacity: self.cache.popitem(last=False) self.cache[key] = value
C++:
class LRUCache{ public: LRUCache(int capacity) { cap = capacity; } int get(int key) { auto it = m.find(key); if (it == m.end()) return -1; l.splice(l.begin(), l, it->second); return it->second->second; } void set(int key, int value) { auto it = m.find(key); if (it != m.end()) l.erase(it->second); l.push_front(make_pair(key, value)); m[key] = l.begin(); if (m.size() > cap) { int k = l.rbegin()->first; l.pop_back(); m.erase(k); } } private: int cap; list<pair<int, int> > l; unordered_map<int, list<pair<int, int> >::iterator> m; };
All LeetCode Questions List 题目汇总