LeetCode: LRU Cache
Title:
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.
使用双向链表+map.
双向链表用于存储数据结点,并且它是按照结点最近被使用的时间来存储的。 如果一个结点被访问了, 我们有理由相信它在接下来的一段时间被访问的概率要大于其它结点。于是, 我们把它放到双向链表的头部。当我们往双向链表里插入一个结点, 我们也有可能很快就会使用到它,同样把它插入到头部。 我们使用这种方式不断地调整着双向链表,链表尾部的结点自然也就是最近一段时间, 最久没有使用到的结点。那么,当我们的Cache满了, 需要替换掉的就是双向链表中最后的那个结点(不是尾结点,头尾结点不存储实际内容)。
如下是双向链表示意图,注意头尾结点不存储实际内容:
1 |
头 --> 结 --> 结 --> 结 --> 尾 |
2 |
结 点 点 点 结 |
3 |
点 <-- 1 <-- 2 <-- 3 <-- 点 |
假如上图Cache已满了,我们要替换的就是结点3。
哈希表的作用是什么呢?如果没有哈希表,我们要访问某个结点,就需要顺序地一个个找, 时间复杂度是O(n)。使用哈希表可以让我们在O(1)的时间找到想要访问的结点, 或者返回未找到。
struct Node{ int key; int val; Node* pre; Node* next; Node(int k,int v):key(k),val(v),pre(NULL),next(NULL){} }; class LRUCache{ private: map<int, Node*> list_map; Node* head; Node* end; int cap; void remove(Node* node){ list_map.erase(node->key); Node* pre = node->pre; Node* next = node->next; if (pre != NULL){ pre->next = next; }else{ head = next; } if (next != NULL){ next->pre = pre; }else{ end = pre; } delete node; } void setHead(Node* node){ list_map[node->key] = node; node->pre = NULL; node->next = head; if (head) head->pre = node; head = node; if (end == NULL){ end = node; } } public: LRUCache(int capacity) { cap = capacity; head = NULL; end = NULL; } int get(int key) { if (list_map.find(key) != list_map.end()){ set(key,list_map[key]->val); return list_map[key]->val; }else return -1; } void set(int key, int value) { Node *node; if (list_map.find(key) != list_map.end()){ remove(list_map[key]); } node = new Node(key,value); setHead(node); if (list_map.size() > cap){ remove(end); } } };
双向链表的实现!果然有点生疏。注意head和end为空的情况。
在set函数中,我每次都是先加入,在判断是否需要删除。另外对map 的诸多操作都包装在remove,insertHead函数中。