【leetcode】146. LRU緩存機制

題目描述

運用你所掌握的數據結構,設計和實現一個  LRU (最近最少使用) 緩存機制 。
實現 LRUCache 類:node

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

思路

這題有幾個須要注意的點:this

  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;
};
相關文章
相關標籤/搜索