关于LeetCode146.LRU Cache 的C++实现

LRU Cache 的功能特点是淘汰最近最少使用的页面,要求访问缓存中的元素和加入缓存表都达到O(1) 的时间复杂度。
我们知道,hashtable可以实现O(1)的查找速度,然而单单使用hashtable无法实现对已缓存的对象实现排序的目的。而排序、O(1)时间插入和删除节点让我们联想到List,如何设计一种数据结构,将这两种数据结构的优点结合在一起呢?
我们可以设计一种hashtable,key指定为缓存对象的key,value指定为链表节点的地址。labuladong的这张图很好的描述了这种数据结构。image
值得一提的是,C++没有提供与Java中LinkedHashMap功能类似的容器,如果我们要强行使用STL,可以参考这篇 https://stackoverflow.com/questions/42072073/equivalent-linkedhashmap-in-c 我个人倾向于自己造一个轮子实现双向链表。这里有个比较坑的点,双向链表的首尾指针最好人为指定为空节点并且不再变动。否则处理的操作会相当相当的麻烦(我调了一个多钟头才调好)
以下是我的代码实现,没有给双向链表设定空头尾节点的版本(虽然最终AC了,但真的好麻烦),不建议使用这个版本!这是坑

struct Node{
    int val;
    int key;
    Node* front = nullptr;
    Node* next = nullptr;
    Node() {}
    Node(int _key, int _val) : key(_key), val(_val) {};
};

class List{
public: 
    List() : num(0) {}
    Node* push_back(int _key, int _val){
        Node* n = new Node(_key, _val);
        if(head == nullptr){
            head = tail = n;
        }
        else{
            tail->next = n;
            n->front = tail;
            tail = n;
        }
        ++num;
        return n;
    }

    void mv_to_tail(Node* n){
        if(n->next == nullptr) return;
        if(head == n) head = head->next;
        n->next->front = n->front;
        if(n->front != nullptr) n->front->next = n->next;
        n->front = tail;
        n->next = nullptr;
        tail->next = n;
        tail = n;
    }

    int del_head(){
        int res = head->key;
        if(head == tail){
            delete head;
            head = nullptr;
            tail = nullptr;
            return res;
        }
        Node *d = head;
        head = head->next;
        head->front = nullptr;
        delete d;
        num--;
        return res;
    }

    int getNum() { return num; }

private:
    Node* head = nullptr;
    Node* tail = nullptr;
    int num;
};

class LRUCache {
public:
    LRUCache(int _capacity) : capacity(_capacity) { }
    
    int get(int key) {
        if(addrTable.count(key)){
            data.mv_to_tail(addrTable[key]);
            return addrTable[key]->val;
        }
        else
            return -1;
    }
    
    void put(int key, int value) {
        if(addrTable.count(key)){
            data.mv_to_tail(addrTable[key]);
            addrTable[key]->val = value;
        }
        else if(data.getNum() >= capacity){
            addrTable.erase(data.del_head());
            
            addrTable[key] = data.push_back(key, value);
        }
        else{
            addrTable[key] = data.push_back(key, value);
        }
    }
private:
    int capacity;
    List data;
    unordered_map<int, Node*> addrTable;
};


/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

这个版本是使用了空节点的版本,相对容易理解

struct Node{
    int val;
    int key;
    Node* front = nullptr;
    Node* next = nullptr;
    Node() {}
    Node(int _key, int _val) : key(_key), val(_val) {};
};

class List{
public: 
    List() : num(0) {
        head = new Node(-1, -1);
        tail = new Node(-1, -1);
        head->next = tail;
        tail->front = head;
    }
    Node* push_back(int _key, int _val){
        ++num;
        Node* n = new Node(_key, _val);
        n->front = tail->front;
        n->next = tail;
        tail->front = n;
        n->front->next = n;
        return n;
    }

    void mv_to_tail(Node* n){
        n->front->next = n->next;
        n->next->front = n->front;

        n->front = tail->front;
        n->next = tail;
        tail->front = n;
        n->front->next = n;
    }

    int del_head(){
        --num;
        Node* d = head->next;
        d->front->next = d->next;
        d->next->front = d->front;
        int res = d->key;
        delete d;
        return res;
    }

    int getNum() { return num; }

private:
    Node* head = nullptr;
    Node* tail = nullptr;
    int num;
};

class LRUCache {
public:
    LRUCache(int _capacity) : capacity(_capacity) { }
    
    int get(int key) {
        if(addrTable.count(key)){
            data.mv_to_tail(addrTable[key]);
            return addrTable[key]->val;
        }
        else
            return -1;
    }
    
    void put(int key, int value) {
        if(addrTable.count(key)){
            data.mv_to_tail(addrTable[key]);
            addrTable[key]->val = value;
        }
        else if(data.getNum() >= capacity){
            addrTable.erase(data.del_head());
            
            addrTable[key] = data.push_back(key, value);
        }
        else{
            addrTable[key] = data.push_back(key, value);
        }
    }
private:
    int capacity;
    List data;
    unordered_map<int, Node*> addrTable;
};


/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

引用&推荐阅读
https://labuladong.github.io/algo/2/22/58/

posted @   柚子z  阅读(87)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!
点击右上角即可分享
微信分享提示