目录
仿真结果:
146题:
运用你所掌握的数据结构,设计和实现一个 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
关于LRU缓存:
LRU原理
操作系统中关于LRU:假设内存只能容纳3个页大小,按照 7 0 1 2 0 3 0 4 的次序访问页。假设内存按照栈的方式来描述访问时间,在上面的,是最近访问的,在下面的是,最远时间访问的,LRU就是这样工作的。保证栈顶的数据最热,栈底的数据最冷;
设计思路:
使用 HashMap 存储 key,这样可以做到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点,利用空间hash_map的空间换来快速如图所示的访问;
LRU 存储是基于双向链表实现的。其中 head 代表双向链表的表头,tail 代表尾部。首先预先设置 LRU 的容量,如果存储满了,可以通过 O(1) 的时间淘汰掉双向链表的尾部,每次新增和访问数据,都可以通过 O(1)的效率把新的节点增加到对头,或者把已经存在的节点移动到队头。
- put(key, value),首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key。
- get(key),通过 HashMap 找到 LRU 链表节点,因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值
代码实现:
struct DlinkdNode {
int key;
int val;
DlinkdNode* pre;
DlinkdNode* next;
};
class LRUCache {
private:
unordered_map<int, DlinkdNode*>cache;
int capacity;
int size;
DlinkdNode*head;
DlinkdNode*tail;
//链表中添加节点
void addNode(DlinkdNode* node)
{
node->pre = head;
node->next = head->next;
head->next->pre = node;
head->next = node;
}
//链表中删除节点
void remove(DlinkdNode*node)
{
node->pre->next = node->next;
node->next->pre = node->pre;
//delete node;此处不能删除,节点的删除交给hash_map进行,否则后序hash_map无法访问此节点
}
//链表中将节点调制头结点,数据变为最热的数据
void moveTohead(DlinkdNode*node)
{
this->remove(node);
this->addNode(node);
}
//删除链表尾节点
DlinkdNode* popTail()
{
DlinkdNode*res = tail->pre;
this->remove(res);
return res;
}
public:
LRUCache(int capacity) {
size = 0;
this->capacity = capacity;
head = new DlinkdNode();
head->pre = nullptr;
tail = new DlinkdNode();
tail->next = nullptr;
head->next = tail;
tail->pre = head;
}
int get(int key) {
unordered_map<int, DlinkdNode*>::iterator it = cache.find(key);
if (it == cache.end())
return -1;
else {
moveTohead(it->second);
return it->second->val;
}
}
void put(int key, int value) {
unordered_map<int, DlinkdNode*>::iterator it = cache.find(key);
if (it == cache.end()) {
DlinkdNode*Node = new DlinkdNode();
Node->key = key;
Node->val = value;
this->cache.insert({ key,Node });
addNode(Node);
++size;
if (size > capacity)//超出容量将尾部最冷数据删除
{
DlinkdNode* TailNode = popTail();
cache.erase(TailNode->key);
--size;
}
}
else {
it->second->val = value;//更新缓存key对应的val,并移动到链表头,最热
moveTohead(it->second);
}
}
};
参考:
https://zhuanlan.zhihu.com/p/34133067?utm_source=wechat_session&utm_medium=social&s_r=0