LRU缓存机制(c++实现)

力扣146;核心结构是双向链表+哈希表实现(插入和查找复杂度均为O(1))

思路是:
维护一个双向链表(使得插入和删除节点时间为常数),每次插入或者查询的节点放在链表头部;
最长时间未使用的放在尾部;链表长度不得超过限制;链表中所有节点存放在
unordered_map中(使得查询时间为常数);

(1)插入节点时,先看节点是否在链表(哈希表)中, 若在,直接更新值。
先检查链表是否达到限制长度:若达到,则删除尾节点,并从哈希表中移除,再插入新建节点;若未达到,则直接插入;
(2)查询节点时,则哈希表直接查询;但之后将此节点移至链表头(其中涉及到删除节点,添加节点;但无需从哈希表中删除)

struct node{
    int key;
    int value;
    node* pre;
    node* next;
    node(int k, int v) : key(k), value(v), pre(NULL), next(NULL){}
};

class LRUCache {
public:
    LRUCache(int capacity) {
        maxSize = capacity;
        beforeHead_ = new BiLisNode(-1, -1);
        afterTail_ = new BiLisNode(-1, -1);
        beforeHead_->next = afterTail_;
        afterTail_->prev = beforeHead_;
    }
public:
    void set(int key, int value)
    {
        //找到则更新
        if(mp.find(key) != mp.end()){
            mp[key]->value = value;
            node* tmp = mp[key];
            removeNode(tmp);
            addNode(tmp);
            return;
        }
        
        //没找到则新建
        node* tmp = new node(key, value);
        //判断是否达到大小限制
        if(mp.size() >= maxSize){
        	//达到则删除尾节点
            mp.erase(m_afterTail->pre->key);
            removeNode(m_afterTail->pre);
        }
        //在头部插入新节点,并加入哈希表
        addNode(tmp);
        mp[key] = tmp;
    }
    
    int get(int key)
    {
        if(mp.find(key) == mp.end())
            return -1;
        node* tmp = mp[key];
        removeNode(tmp);
        addNode(tmp);
        return tmp->value;
    }
    
    //不包含移除出哈希表操作;因为在get操作中有删除操作,但又有增加操作
    void removeNode(node* x)
    {
        if(x != NULL){
            x->pre->next = x->next;
            x->next->pre = x->pre;
        }
    }
    
    //添加节点到头部
    void addNode(node* x)
    {
        if(x != NULL){
            x->pre = m_beforeHead;
            x->next = m_beforeHead->next;
            m_beforeHead->next->pre = x;
            m_beforeHead->next = x;
        }
    }
    
private:
    unordered_map<int, node*> mp;
    node* m_beforeHead;
    node* m_afterTail;
    int maxSize;
};

代码实现有一些小技巧:
1.使用额外的头前节点和尾后节点(便于插入头结点和删除尾节点,不用做额外的判断)
2.将key,value都存储在节点中,再通过key做映射,这样,在删除节点时,不用通过在map中找到对应的value再删除(O(N)复杂度)

之前的做法:只是将value存储在节点中,然后key与节点做映射,删除节点时,遍历了map找到value等于此节点的key,再erase

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LRU 缓存是一种常见的缓存淘汰算法,它的设计思想是将最近最少使用的数据从缓存中淘汰出去,以保证缓存的数据都是热点数据,从而提高缓存的命中率。 下面是一个基于双向链表哈希表实现LRU 缓存C++ 设计实现: ```cpp #include <unordered_map> struct Node { int key; int value; Node* prev; Node* next; Node(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {} }; class LRUCache { public: LRUCache(int capacity) : size_(capacity), head_(nullptr), tail_(nullptr) {} int get(int key) { auto iter = cache_.find(key); if (iter == cache_.end()) { return -1; } else { Node* node = iter->second; move_to_head(node); return node->value; } } void put(int key, int value) { auto iter = cache_.find(key); if (iter == cache_.end()) { Node* node = new Node(key, value); add_to_head(node); cache_[key] = node; if (cache_.size() > size_) { Node* tail = remove_tail(); cache_.erase(tail->key); delete tail; } } else { Node* node = iter->second; node->value = value; move_to_head(node); } } private: int size_; Node* head_; Node* tail_; std::unordered_map<int, Node*> cache_; void add_to_head(Node* node) { if (head_ == nullptr) { head_ = node; tail_ = node; } else { node->next = head_; head_->prev = node; head_ = node; } } void move_to_head(Node* node) { if (node == head_) { return; } else if (node == tail_) { tail_ = tail_->prev; tail_->next = nullptr; } else { node->prev->next = node->next; node->next->prev = node->prev; } node->next = head_; head_->prev = node; node->prev = nullptr; head_ = node; } Node* remove_tail() { Node* node = tail_; if (head_ == tail_) { head_ = nullptr; tail_ = nullptr; } else { tail_ = tail_->prev; tail_->next = nullptr; } return node; } }; ``` 在这个实现中,我们使用了一个双向链表保存缓存中的数据,并使用一个哈希表来提高查找效率。在 `get` 操作中,如果缓存中不存在目标数据则返回 -1,否则将目标数据移到链表头部并返回其值。在 `put` 操作中,如果缓存中不存在目标数据则创建一个新的节点并将其添加到链表头部,如果缓存已满则删除链表尾部的节点。如果缓存中已存在目标数据则将其值更新并将其移动到链表头部。 需要注意的是,在 `move_to_head` 和 `remove_tail` 操作中,我们需要判断目标节点是否已经在链表的头部或尾部,以避免对空指针进行操作。此外,在每次操作中,我们还需要更新哈希表中对应节点的指针。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值