leetcode_146 LRU缓存

1. 题意

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存

int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

2. 题解

单次插入需要 O ( 1 ) O(1) O(1),

查找也需要是 O ( 1 ) O(1) O(1)。

因此我们需要哈希表和双向链表来完成这一操作。

2.1 我的解

直接自己写个双向链表。

同时我们需要维护链表头和尾。

需要注意的是一些异常情况,比如链表空,或者只有一个头节点。

cpp 复制代码
class LRUCache {


public:
    struct LRUNode {
        LRUNode(int k, int v):key(k),value(v) {

        }
        int key;
        int value;
    };

    struct LRUList {
        LRUList *pre;
        LRUList *next;
        LRUNode *node;
    };

    LRUCache(int capacity):max_cap_(capacity){
        
    }
    
    int get(int key) {
        // cout << "get " << key << "\n";
        if ( hs.count(key) ) {
            LRUList *cur = hs[key];
            if ( cur != head) {
                if ( cur == tail) 
                    tail = cur->pre;
            
                cur->pre->next = cur->next;
                if (cur->next)
                    cur->next->pre = cur->pre;
                
                head->pre  = cur;
                cur->next = head;
                cur->pre  = NULL;
                head = cur;                
            }
            return cur->node->value;
        }
        return -1;
    }
    void put(int key, int value) {
        // cout << "put: [ " << key <<", " << value << " ]" << "\n"; 
        if ( hs.count(key) ) {
            LRUList *cur = hs[key];

            cur->node->value = value;

            if (cur == head)
                return;

            if ( cur == tail) {
                tail = cur->pre;
            }

            cur->pre->next = cur->next;
            if (cur->next)
                cur->next->pre = cur->pre;
            
            head->pre  = cur;
            cur->next = head;
            cur->pre  = NULL;
            head = cur;
        }
        else {
            LRUList *cur = new LRUList;
            cur->pre = cur->next = NULL;
            cur->node = new LRUNode(key, value);
            ++cur_cap_;

            hs[key] = cur;

            if (head)
                head->pre = cur;
            if (tail == NULL)
                tail = cur;

            cur->next = head;
            head = cur;

            if ( cur_cap_ > max_cap_) {
                LRUList *del = tail;

                // cout << "del " << del->node->key << "\n";

                hs.erase(del->node->key);

                tail = del->pre;
                if (tail)
                    tail->next = NULL;
                del->pre = NULL;
                del->next = NULL;
                delete del->node;
                delete del;
                --cur_cap_;
            }
        }   
    }
private:
    int max_cap_;
    int cur_cap_{};

    unordered_map<int,LRUList *> hs;
    LRUList *head{};
    LRUList *tail{};
};

/**
 * 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);
 */
2.2 0x3f的解

我看0x3f的解,主要是模块化,还有加了一个哨兵节点就省去了首尾节点的判断。

cpp 复制代码
class LRUCache {
public:
struct LRUNode {

        LRUNode():pre(NULL),next(NULL) {

        }
        LRUNode(int key, int val):k(key),v(val), pre(NULL), next(NULL){
            
        }
        int k;
        int v;
        LRUNode *pre;
        LRUNode *next;
    };
private:
    int cap_;

    unordered_map<int, LRUNode *> key_to_node;
    LRUNode *dumNode;
    
public:

    

    LRUCache(int capacity) : cap_(capacity), dumNode(new LRUNode()) {
        dumNode->pre = dumNode;
        dumNode->next = dumNode;
    }

    void remove(LRUNode *cur) {
        cur->pre->next = cur->next;
        cur->next->pre = cur->pre;
        cur->pre = cur->next = NULL;
    }

    void push_front(LRUNode *cur) { 

        cur->next = dumNode->next;
        cur->pre  = dumNode;

        dumNode->next->pre =cur;
        dumNode->next = cur;
    }

    int get(int key) {
        //cout << "get " << key << "\n";
        auto it = key_to_node.find(key);
        if ( it != key_to_node.end()) {
            LRUNode *cur = it->second;

            remove( cur );
            push_front( cur );

            return cur->v;
        }

        return -1;
    }

    void put(int key, int value) {
        //cout << "put [ " << key << ", " << value << " ]\n";

        auto it = key_to_node.find(key);
        if ( it != key_to_node.end()) {
            LRUNode *cur  = it->second;
            cur->v = value;

            remove( cur );
            push_front( cur );
        }
        else {
            LRUNode *cur = new LRUNode(key, value);
            key_to_node[key] = cur;
            push_front(cur);

            if ( key_to_node.size() > cap_) {
                LRUNode *del_node = dumNode->pre;
                key_to_node.erase( del_node->k);
                remove( del_node);
                delete del_node;
            }
        }
    }
};

还有一种就是使用标准库的双向链表了,不过标准库的api真的感觉好难用!

cpp 复制代码
class LRUCache {


public:
    struct LRUNode {
        LRUNode(int k, int v):key(k),value(v) {

        }
        int key;
        int value;
    };

    struct LRUList {
        LRUList *pre;
        LRUList *next;
        LRUNode *node;
    };

    LRUCache(int capacity):max_cap_(capacity){
        
    }

    int get(int key) {
        // cout << "get " << key << "\n";
        auto it = key_to_it.find(key);
        if ( it  == key_to_it.end()) {
            return -1;
        }
        
        cached_list.splice( cached_list.begin(), cached_list, it->second);

        return cached_list.begin()->value;
    }
    void put(int key, int value) {
        
        auto it = key_to_it.find( key );
        if ( it != key_to_it.end()) {
            it->second->value = value;

            cached_list.splice( cached_list.begin(), cached_list, it->second);
        }
        else {
            auto nNode = new LRUNode(key, value);
            cached_list.push_front( *nNode );
            key_to_it[key] = cached_list.begin();

            if ( cached_list.size() > max_cap_) {
                key_to_it.erase(cached_list.back().key );
                cached_list.pop_back();
            }
        }
    }
private:
    int max_cap_;
    unordered_map<int, list<LRUNode>::iterator> key_to_it;

    list<LRUNode> cached_list;
};

/**
 * 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);
 */

3. 参考

0x3f

cpp-splice

相关推荐
重生之后端学习7 分钟前
239. 滑动窗口最大值[困难]✅
java·数据结构·算法·leetcode·职场和发展
fpcc33 分钟前
算法和数据结构—动态规划法
数据结构·算法·动态规划
星星.7223 小时前
C++算法竞赛|二分查找与二分答案:边界模板、浮点二分、STL
数据结构·c++·算法
sel_93 小时前
【OPD论文导读(二)】OPD(On-Policy Distillation)全景调研:十篇论文讲透“在自己生成的内容上学习“这件事
人工智能·python·深度学习·学习·算法·语言模型
金幄科技4 小时前
RAG第四篇:重排序 Rerank——粗召回之后,为什么还需要一次精排
人工智能·算法·ai·ai编程
M78佐菲4 小时前
Linux学习笔记:文件IO
linux·笔记·学习·算法
lhldsg5 小时前
课程排课系统实战指南:从数据库设计到算法调优全流程解析
java·数据库·算法·小程序
BSD_HY5 小时前
薄膜开关矩阵扫描电路设计中的防鬼键措施
人工智能·算法·矩阵·人机交互·薄膜开关·源头工厂·深圳工厂
linux-hzh5 小时前
百日算法修炼 · Day 17
数据结构·算法
XUEYUAN52125 小时前
代理日志分析与监控:代理池健康状态巡检与分级告警体系搭建(运维实战)
运维·网络·网络协议·tcp/ip·算法·架构