146. LRU缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据
get
和 写入数据put
。获取数据
get(key)
- 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据put(key, value)
- 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // 返回 1 cache.put(3, 3); // 该操作会使得密钥 2 作废 cache.get(2); // 返回 -1 (未找到) cache.put(4, 4); // 该操作会使得密钥 1 作废 cache.get(1); // 返回 -1 (未找到) cache.get(3); // 返回 3 cache.get(4); // 返回 4
解法一:
//时间复杂度O(1), 空间复杂度O(n)
class LRUCache {
public:
LRUCache(int capacity) {
head = new Node(-1, -1, nullptr, nullptr);
last = head;
this->capacity = capacity;
size = 0;
}
int get(int key) {
if(!um.count(key)) return -1;//不存在,直接返回
if(um[key] == last) return um[key]->val;//已经是最后一个,不用调整位置
um[key]->pre->next = um[key]->next;
um[key]->next->pre = um[key]->pre;
um[key]->next = nullptr;
um[key]->pre = last;
last->next = um[key];
last = um[key];
return um[key]->val;
}
void put(int key, int value) {
if(um.count(key)) {
um[key]->val = value;
get(key);//调整位置
return;
}
if(size == capacity) {
um.erase(head->next->key);
if(head->next == last) last = head;//只有一个元素,要处理last指针
head->next = head->next->next;
if(head->next) head->next->pre = head;
size--;
}
last->next = new Node(key, value, last, nullptr);
last = last->next;
um[key] = last;
size++;
}
private:
struct Node {
int key;
int val;
Node* pre;
Node* next;
Node(int key, int val, Node* pre, Node* next) : key(key), val(val), pre(pre), next(next) {}
};
Node* head;
Node* last;
unordered_map<int, Node*> um;
int capacity;
int size;
};
/**
* 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);
*/
解法二:
class LRUCache {
public:
LRUCache(int capacity) : cap(capacity) {}
int get(int key) {
if (!um.count(key)) return -1;
ls.splice(ls.end(), ls, um[key]);
return um[key]->val;
}
void put(int key, int value) {
if (um.count(key)) {
um[key]->val = value;
get(key);
return;
}
if (ls.size() == cap) {
um.erase(ls.begin()->key);
ls.erase(ls.begin());
}
um[key] = ls.insert(ls.end(), Node(key, value));
}
private:
struct Node {
Node(int k, int v) : key(k), val(v) {}
int key;
int val;
};
int cap;
unordered_map<int, list<Node>::iterator> um;
list<Node> ls;
};
/**
* 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);
*/
解法一:
用一个双向链表作访问记录,靠近链表尾的元素是最近访问过的元素,链表首元素(head->next)是最近最小使用的元素,如果空间满了需要首先将首元素删除,再向链表尾插入新元素。为了实现O(1)的时间要求,使用哈希表um来记录<键, 结点指针>的对,在插入和删除时要更新um。其余细节较多,见代码。
解法二: 思路同解法一。使用std::list。
2019/12/22 21:20