关于c++:leetcode146-LRU缓存机制

6次阅读

共计 2322 个字符,预计需要花费 6 分钟才能阅读完成。

题目形容

使用你所把握的数据结构,设计和实现一个  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);    // 返回 1
lRUCache.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);    // 返回 3
lRUCache.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;
};
正文完
 0