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

相关推荐
近津薪荼12 小时前
优选算法——双指针8(单调性)
数据结构·c++·学习·算法
格林威12 小时前
Baumer相机铆钉安装状态检测:判断铆接是否到位的 5 个核心算法,附 OpenCV+Halcon 的实战代码!
人工智能·opencv·算法·计算机视觉·视觉检测·工业相机·堡盟相机
星空露珠12 小时前
速算24点检测生成核心lua
开发语言·数据库·算法·游戏·lua
what丶k12 小时前
SpringBoot3 缓存抽象深度实践:Caffeine+Redis多级缓存,穿透/雪崩/击穿防御全方案
数据库·redis·缓存
咖啡の猫12 小时前
Redis简单介绍
数据库·redis·缓存
happygrilclh12 小时前
高压高频电源的pid算法
算法
爱吃大芒果13 小时前
Flutter for OpenHarmony 实战: mango_shop 购物车模块的状态同步与本地缓存处理
flutter·缓存·dart
格林威13 小时前
Baumer相机铸件气孔与缩松识别:提升铸造良品率的 6 个核心算法,附 OpenCV+Halcon 实战代码!
人工智能·opencv·算法·安全·计算机视觉·堡盟相机·baumer相机
葫三生13 小时前
存在之思:三生原理与现象学对话可能?
数据库·人工智能·神经网络·算法·区块链
Evand J13 小时前
【MATLAB例程】无人机三维路径规划|A*,RRT(快速随机树算法), APF(人工势场法)算法对比|可自定义起终点、障碍物坐标。附下载链接
算法·matlab·无人机·astar·路径规划·rrt·apf