题目形容
使用你所把握的数据结构,设计和实现一个 LRU (最近起码应用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字曾经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到下限时,它应该在写入新数据之前删除最久未应用的数据值,从而为新的数据值留出空间。
示例:
输出["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"][[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]输入[null, null, null, 1, null, -1, null, -1, 3, 4]解释LRUCache lRUCache = new LRUCache(2);lRUCache.put(1, 1); // 缓存是 {1=1}lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}lRUCache.get(1); // 返回 1lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}lRUCache.get(2); // 返回 -1 (未找到)lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}lRUCache.get(1); // 返回 -1 (未找到)lRUCache.get(3); // 返回 3lRUCache.get(4); // 返回 4
思路
这题有几个须要留神的点:
- cache是有容量限度的
- 须要依据最近是否应用过去排定数据的程序
- get和put都看做是一次拜访
为了保障get和put都满足O(1)的工夫复杂度,又因为是依据最近是否拜访来决定淘汰程序,最直观的计划是应用队列。但队列有个致命缺点——只能操作队头元素和队尾元素,这就意味着get()操作时非常麻烦。
另一个经典的思路是双链表+hashmap,双链表的插入删除工夫复杂度都是O(1),而hashmap指针取出结点指针保障了get()的工夫复杂度为O(1)。
另外有几点须要留神:
- 双链表应设置头结点和尾结点,能够防止空链表的边界状况,省去很多麻烦
- get()算作一次拜访,拜访后须要让该结点从新置于链表的尾部(或头部,看怎么设置和了解,上面代码采纳和循环链表同样的概念,即表尾进表头出,也就是表尾代表最新拜访的结点,表头代表下一个移除的结点)。代码操作也非常容易,只需将结点在链表中移除(不须要delete),而后再插入到表尾即可
- 在超出缓存限度须要删除结点时,别忘了delete指针,防止内存透露。
代码
struct DLinkNode { int key, value; DLinkNode *prev, *next; DLinkNode(): key(0), value(0), prev(nullptr), next(nullptr) {} DLinkNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}};class LRUCache {public: LRUCache(int capacity) { this->cap = capacity; this->len = 0; tail = new DLinkNode; head = new DLinkNode; tail -> prev = head; head -> next = tail; } int get(int key) { if(hashmap.count(key)) { DLinkNode* node = hashmap[key]; movetoTail(node); return node -> value; } return -1; } void put(int key, int value) { if(!hashmap.count(key)) { DLinkNode* node = new DLinkNode(key, value); addNode(node); hashmap[key] = node; len++; if(len > cap) { removeNode(head -> next); len--; } } else { hashmap[key] -> value = value; movetoTail(hashmap[key]); } } void movetoTail(DLinkNode* node) { node -> prev -> next = node -> next; node -> next -> prev = node -> prev; tail -> prev -> next = node; node -> prev = tail -> prev; tail -> prev = node; node -> next = tail; } void addNode(DLinkNode* node) { tail -> prev -> next = node; node -> prev = tail -> prev; node -> next = tail; tail -> prev = node; } void removeNode(DLinkNode* node) { node -> prev -> next = node -> next; node -> next -> prev = node -> prev; hashmap.erase(node->key); delete node; }private: unordered_map<int, DLinkNode*> hashmap; DLinkNode *head, *tail; int cap; int len;};