LeetCode 146. LRU CacheLRU缓存机制 (C++/Java)
题目:
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and put
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.put(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.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // returns 1 cache.put(3, 3); // evicts key 2 cache.get(2); // returns -1 (not found) cache.put(4, 4); // evicts key 1 cache.get(1); // returns -1 (not found) cache.get(3); // returns 3 cache.get(4); // returns 4
分析:
设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get
和 写入数据 put
。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
按照所给的例子看一下cache内部是如何变化的。
front | back | result | |
put(1, 1) | (1, 1) | ||
put(2, 2) | (2, 2) | (1, 1) | |
get(1) | (1, 1) | (2, 2) | 1 |
put(3, 3) | (3, 3) | (1, 1) | |
get(2) | (3, 3) | (1, 1) | -1 |
put(4, 4) | (4, 4) | (3, 3) | |
get(1) | (4, 4) | (3, 3) | -1 |
get(3) | (3, 3) | (4, 4) | 3 |
get(4) | (4, 4) | (3, 3) | 4 |
很清楚的就理解了LRU的机制,当get的key在cache中已经存在时,就将存储的内容放到最前面,get的key不存在时就返回-1。
put的新的key-value时,如果达到了容量上限,就删除一个最近最少使用的,实际上也是队尾的元素,然后将新的key-value存储到最前面。
因为我们每次要O(1)的复杂度,所以可以使用hashmap来get数据,而当容量达到上限时,要删除最近最少使用的,且要在最前面put进新的数据,要使用一个双向链表,来保证O(1)的时间复杂度。
java中可以使用LinkeHashMap来实现LRU缓存。
程序:
C++
class LRUCache { public: LRUCache(int capacity) { cap = capacity; } int get(int key) { auto it = map.find(key); if(it == map.end()) return -1; l.splice(l.begin(), l, it->second); return it->second->second; } void put(int key, int value) { auto it = map.find(key); if(it != map.end()){ l.erase(it->second); } l.push_front(make_pair(key, value)); map[key] = l.begin(); if (map.size() > cap) { int k = l.rbegin()->first; l.pop_back(); map.erase(k); } } private: int cap; list<pair<int, int>> l; unordered_map<int, list<pair<int, int>>::iterator> map; };
Java
class LRUCache { public LRUCache(int capacity) { this.cap = capacity; this.map = new HashMap<>(); this.dummyHead = new Node(0, 0); this.tail = new Node(0, 0); dummyHead.prev = null; dummyHead.next = tail; tail.prev = dummyHead; tail.next = null; } public int get(int key) { if(!map.containsKey(key)) return -1; Node node = map.get(key); removeNode(node); addToHead(node); return node.val; } public void put(int key, int value) { if(map.containsKey(key)){ Node node = map.get(key); removeNode(node); map.remove(key); size--; } Node node = new Node(key, value); map.put(key, node); if(size < cap){ addToHead(node); size++; }else { map.remove(tail.prev.key); removeNode(tail.prev); addToHead(node); } } private void removeNode(Node node){ node.prev.next = node.next; node.next.prev = node.prev; } private void addToHead(Node node){ node.next = dummyHead.next; node.next.prev = node; dummyHead.next = node; node.prev = dummyHead; } class Node{ int key; int val; Node prev; Node next; public Node(int key, int val){ this.key = key; this.val = val; } } private int cap; private Map<Integer, Node> map; private Node dummyHead; private Node tail; private int size; }
class LRUCache { public LRUCache(int capacity) { this.capacity = capacity; map = new LinkedHashMap<>(); } public int get(int key) { if(map.containsKey(key)) { int value = map.get(key); map.remove(key); map.put(key, value); return value; } return -1; } public void put(int key, int value) { if(map.containsKey(key)) { map.remove(key); } map.put(key, value); if(map.size() > capacity) { map.remove(map.keySet().iterator().next()); } } private int capacity; private LinkedHashMap<Integer, Integer> map; }