• LRU cache 实现


    前言

    LRU 是 Least Recently Used 的简写,字面意思是最近最少使用。
    通常用于缓存的淘汰策略实现,由于缓存的内存非常宝贵,所以需要根据某种规则来剔除数据保证内存不被撑满。

    代码实现

    #ifndef _LRU_CACHE_H_
    #define _LRU_CACHE_H_
    
    #include <unordered_map>
    /*
    * LRU是Least Recently Used的简写,字面意思是最近最少使用,通常用于缓存淘汰策略
    * 维护一个双向链表,并用无序关联式容器unordered_map存储链表的节点
    */
    template <typename Key, typename Value>
    class LRUCache {
    private:
        struct Node {
            Key key;
            Value value;
            Node* prev;
            Node* next;
            Node(Key k, Value v)
                : key(k)
                , value(v)
                , prev(nullptr)
                , next(nullptr)
            {
            }
        };
    
    public:
        LRUCache(size_t capacity)
            : capacity_(capacity)
            , head_(nullptr)
            , tail_(nullptr)
        {
        }
        ~LRUCache()
        {
            while (head_ != nullptr) {
                Node* next = head_->next;
                delete head_;
                head_ = next;
            }
        }
    
        /*
        * @brief 获取缓存值
        * 根据key获取value,不存在返回nullptr
        * 存在,则在双向链表中删除该节点,再将节点添加到表头
        */
        Value* get(Key key)
        {
            auto it = datas_.find(key);
            if (it == datas_.end()) {
                return nullptr;
            } else {
                Node* node = datas_[key];
                remove(node);
                appendHead(node);
                return &node->value;
            }
        }
    
        /*
        * @brief 将键值对放进缓存中
        * 缓存已存在,更新value,并在双向链表中删除该节点,再将节点添加到表头
        * 不存在,创建节点node,如果当前缓存大小小于缓存容量,直接将节点添加到
        * 表头即可,否则将双向链表的尾结点在关联式容器hashMap中删除,然后在双
        * 向链表中也删除尾节点,最后将新节点添加到表头和hashMap中
        */
        void put(Key key, Value value)
        {
            auto it = datas_.find(key);
            if (it != datas_.end()) {
                Node* node = it->second;
                node->value = value;
                remove(node);
                appendHead(node);
            } else {
                Node* node = new Node(key, value);
                if (datas_.size() < capacity_) {
                    appendHead(node);
                    datas_[key] = node;
                } else {
                    datas_.erase(tail_->key);
                    Node* delNode = tail_;
                    remove(delNode);
                    delete delNode;
                    appendHead(node);
                    datas_[key] = node;
                }
            }
        }
    
    private:
        /*
        * 将节点添加到双向链表的头部
        */
        void appendHead(Node* node)
        {
            if (head_ == nullptr)
                head_ = tail_ = node;
            else {
                node->next = head_;
                head_->prev = node;
                head_ = node;
            }
        }
    
        /*
        * 在双向链表删除node节点,但不销毁节点,主要是为了其他方法可以通用
        */
        void remove(Node* node)
        {
            if (head_ == tail_) {
                head_ = tail_ = nullptr;
            } else if (head_ == node) {
                head_ = node->next;
                head_->prev = nullptr;
            } else if (tail_ == node) {
                tail_ = node->prev;
                tail_->next = nullptr;
            } else {
                node->prev->next = node->next;
                node->next->prev = node->prev;
            }
    
            node->next = nullptr;
            node->prev = nullptr;
        }
    
    private:
        size_t capacity_; // 缓存容量
        Node* head_; // 双向链表的头结点
        Node* tail_; // 双向链表的尾结点
        std::unordered_map<int, Node*> datas_; // 无序关联式容器
    };
    
    #endif
    

    利用STL list简单实现

    class LRUCache {
    public:
        LRUCache(int capacity) : capacity_(capacity) {
    
        }
        
        int get(int key) {
            auto it = table_.find(key);
            if (it == table_.end()) 
                return -1;
            lru_.remove(key);
            lru_.push_front(key);
            return it->second;
        }
        
        void put(int key, int value) {
            auto it = table_.find(key);
            if (it != table_.end()) {
                lru_.remove(key);
                lru_.push_front(key);
                it->second = value;
            } else {
                if (lru_.size() == capacity_) {
                    int tmp = lru_.back();
                    lru_.pop_back();
                    table_.erase(tmp);
                } 
                lru_.push_front(key);
                table_[key] = value;
            }
        }
    
    private:
        int capacity_;
        list<int> lru_;
        unordered_map<int, int> table_;
    };
    
  • 相关阅读:
    Mac下安装cscope和ctags
    MAC的VIMRC
    我的Linux系统的VIMRC
    关于在VI中查看BIN文件二进制值不对的问题
    没有关心过编写代码方式的好处,你是不是也是这样?
    四十不惑,真的不惑了么?
    jQuery通过text值来设置选定,以及遍历select的选项个数和遍历
    C#解析XML文件
    网页调用本地程序(Windows下浏览器全兼容)
    C#获取文件的md5
  • 原文地址:https://www.cnblogs.com/evenleee/p/14171628.html
Copyright © 2020-2023  润新知