感想
这道题在leetcode是hard的难度,通过率只有21.2%,我记得在某次笔试的时候做到了这一道题,那时候做得不怎么好,我这里把记录一下我以前写的代码,就当复习一下
题目
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.
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
代码
class LRUCache {
private:
list<pair<int,int>> cache;
int size;
unordered_map<int,list<pair<int,int>>::iterator> hash;
public:
LRUCache(int capacity) {
size=capacity;
}
int get(int key) {
auto it=hash.find(key);
if(it==hash.end()) return -1;
cache.splice(cache.begin(),cache,it->second);
return it->second->second;
}
void put(int key, int value) {
auto it=hash.find(key);
if(it!=hash.end()){
it->second->second=value;
cache.splice(cache.begin(),cache,it->second);
return;
}
cache.insert(cache.begin(),make_pair(key,value));
hash[key]=cache.begin();
if(cache.size()>size){
hash.erase(cache.back().first);
cache.pop_back();
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
analysis
如果来了一个get请求, 我们仍然先去查hash表, 如果key存在hash表中, 那么需要将这个结点在链表的中的位置移动到链表首部.否则返回-1.
另外一个非常关键的降低时间复杂度的方法是在hash中保存那个key在链表中对应的指针, 我们知道链表要查找一个结点的时间复杂度是O(n), 所以当我们需要移动一个结点到链表首部的时候, 如果直接在链表中查询那个key所对于的结点, 然后再移动, 这样时间复杂度将会是O(n), 而一个很好的改进方法是在hash表中存储那个key在链表中结点的指针, 这样就可以在O(1)的时间内移动结点到链表首部.
STL技巧:
1、使用map的find方法来判断key是否已经存在,返回值和map的end迭代器比较;
2、使用unordered_map,它是hash_map,存取时间都是O(1),用它存储元素的position迭代器,是为了方便splice函数调用list.splice(position, list, element_pos)函数作用是把list的element_pos处的元素插入到position位置,本题中为了移动元素到list头部
Python实现
这道题还挺麻烦的,我反正是短时间内做不出来,我看了一下解析,主要是利用了双向链表的方式,然后定义一个moveToHead,removeNode,removeTail的操作来辅助完成put和tail的O(1)时间复杂度。
class DlinkNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cache = dict()
self.head = DlinkNode()
self.tail = DlinkNode()
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key: int) -> int:
if key not in self.cache:
return -1
node = self.cache[key]
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.cache:
node = DlinkNode(key, value)
self.cache[key]=node
self.addToHead(node)
self.size +=1
if self.size > self.capacity:
removed = self.removeTail()
self.cache.pop(removed.key)
self.size -=1
else:
node = self.cache[key]
node.value = value
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def moveToHead(self, node):
self.removeNode(node)
self.addToHead(node)
def removeTail(self):
node = self.tail.prev
self.removeNode(node)
return node
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
reference
[leetcode] 146. LRU Cache 解题报告. https://blog.csdn.net/qq508618087/article/details/50995188
LRU的C++实现.https://blog.csdn.net/tinyway/article/details/24536327