题目形容

使用你所把握的数据结构,设计和实现一个  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

思路

这题有几个须要留神的点:

  1. cache是有容量限度的
  2. 须要依据最近是否应用过去排定数据的程序
  3. get和put都看做是一次拜访

为了保障get和put都满足O(1)的工夫复杂度,又因为是依据最近是否拜访来决定淘汰程序,最直观的计划是应用队列。但队列有个致命缺点——只能操作队头元素和队尾元素,这就意味着get()操作时非常麻烦。
另一个经典的思路是双链表+hashmap,双链表的插入删除工夫复杂度都是O(1),而hashmap指针取出结点指针保障了get()的工夫复杂度为O(1)。
另外有几点须要留神:

  1. 双链表应设置头结点和尾结点,能够防止空链表的边界状况,省去很多麻烦
  2. get()算作一次拜访,拜访后须要让该结点从新置于链表的尾部(或头部,看怎么设置和了解,上面代码采纳和循环链表同样的概念,即表尾进表头出,也就是表尾代表最新拜访的结点,表头代表下一个移除的结点)。代码操作也非常容易,只需将结点在链表中移除(不须要delete),而后再插入到表尾即可
  3. 在超出缓存限度须要删除结点时,别忘了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;};