LRU(C++实现)(Least Recently Used,最近最少使用)

算法逻辑:

1.新数据会插入到链表头

2.当缓存数据被访问,将该缓存数据移到链表头部

3.当新数据插入时达到缓存上限了,将尾部数据删除掉(也就是最近最少使用的),新数据放在头部。

利用Map进行节点定位,时间复杂度大大降低,利用双向链表实现LRUCache逻辑,便于频繁实现首尾节点的移除和更新。

#include<iostream>
#include<map>
using namespace std;

typedef struct MyListNode {
    int key;
    int val;
    MyListNode* next;
    MyListNode* pre;
    MyListNode(int k, int v) :key(k), val(v), next(NULL), pre(NULL) {}  //C++里可以这样来初始化结构体,如同一个构造函数
}MyListNode;

class LRUCache {

private:
    int m_capacity;    //缓存容量
    MyListNode* pHead;   //头节点
    MyListNode* pTail;   //尾节点
    map<int, MyListNode*>  mp;   //mp用来存数据,达到find为o(1)级别


public:
    LRUCache(int size)
    {
        m_capacity = size;
        pHead = NULL;
        pTail = NULL;
    }
    ~LRUCache()
    {
        map<int, MyListNode*>::iterator it = mp.begin();  
        for (; it != mp.end();)
        {
            delete it->second; //一定要注意,map释放内存时,先释放内部new的内存,再释放map的内存,先释放的是MyListNode节点内存
            it->second = NULL;//节点指针指向空
            mp.erase(it++);
        }
        delete pHead;
        pHead = NULL;
        delete pTail;
        pTail = NULL;
    }


    void remove(MyListNode* node)
    {
        if (node->pre == NULL)  //若本身是头节点
        {
            pHead = node->next;//将头节点连接到其下一个节点node->next
            pHead->pre = NULL;
        }    
        else if (node->next == NULL)  //若本身是尾节点
        {
            pTail = node->pre;        //新尾节点为node->pre
            pTail->next = NULL;
        }
        else
        {
            node->pre->next = node->next;
            node->next->pre = node->pre;
        }
        
    }

    void sethead(MyListNode* node)
    {
        node->next = pHead;
        node->pre = NULL;
        if (pHead == NULL)
        {
            pHead = node;
        }
        else
        {
            pHead->pre = node;
            pHead = node;
        }
        if (pTail == NULL)
        {
            pTail = pHead;
        }

    }


    void Set(int key, int value)
    {
        map<int, MyListNode*>::iterator it = mp.find(key);//找到key值则返回key所在的节点,否则返回mp.end()
        if (it != mp.end())
        {
            MyListNode* node = it->second;
            node->val = value;
            remove(node);
            sethead(node);
        }
        else
        {
            MyListNode* node = new MyListNode(key, value);//没有找到键值就创建一个新节点
            int len = this->gtesize();
            if (len >= m_capacity)
            {
            
                map<int, MyListNode*>::iterator it = mp.find(pTail->key);  //这里寻找尾节点位置为了快速定位才在MyListNode结构体中定义了Key
                                                                            //否则需要遍历整个双向链表才能找到尾节点
                remove(pTail);
                delete it->second;  //如果大于设定缓存大小,则删除尾节点 ,同样要先释放节点内存,再释放map内存
                mp.erase(it);
            }
            sethead(node);
            mp[key] = node;

        }
    
    }

    int Get(int key)
    {
        map<int, MyListNode*>::iterator it = mp.find(key);//找到key值则返回key所在的节点,否则返回mp.end()
        if (it != mp.end())
        {
            MyListNode* Node = it->second;
            remove(Node);      //移除节点,不是删除
            sethead(Node);        //将找到的节点放到首部
            return Node->val;

        }
        else
            return -1;
    
    }

    int gtesize()
    {
    
        return mp.size();
    }

};

int main()
{
    LRUCache* lru=new LRUCache(3);
    lru->Set(1, 3);
    cout<<lru->gtesize()<<endl;
    lru->Set(2, 4);
    cout<<lru->Get(3)<<endl;
    lru->Set(5, 6);
    cout<< lru->Get(2)<<endl;
    cout << lru->Get(5) << endl;
    cout << lru->gtesize()<<endl;
    lru->Set(4, 7);
    cout << lru->Get(4)<<endl;
    cout << lru->Get(1)<< endl;
    return 0;
    
}

测试:

 设置缓存大小为3:所以最多存储数据为3个

lru->Set(1, 3);                        缓存里存有(1,3)这个节点数据

cout<<lru->gtesize()<<endl;   此时大小为1,只有一个节点存入

lru->Set(2, 4);       缓存里为(2,4)(1,3)两个节点,后存入的放在前面

cout<<lru->Get(3)<<endl;  在缓存里寻找key=3的节点,返回该节点的value,此时缓存里只有key=2,key=1的节点,所以返回 -1

lru->Set(5, 6);       此时缓存里为(5,6)(2,4)(1,3)三个节点,同样后存入的放在前面

cout<< lru->Get(2)<<endl;   在缓存里寻找key=2的节点,返回该节点的value,所以输出为key=2对应的value=4;注意这里使用了(2,4),所以缓存内要把这个节点移到最前面

             所以此时缓存内容为(2,4)(5,6)(1,3)

cout << lru->Get(5) << endl; 在缓存里寻找key=5的节点,返回该节点的value,所以输出为key=5对应的value=6;注意这里使用了(5,6),所以缓存内要把这个节点移到最前面

             所以此时缓存内容为(5,6)(2,4)(1,3)

cout << lru->gtesize()<<endl; 此时大小为3,有三个节点存入

lru->Set(4, 7);       缓存里存有(4,7)这个节点数据,但缓存大小为3 已满,所以删除在缓存最末端的节点(1,3),把新插入的节点放最前面

                所以此时缓存内容为(4,7)(5,6)(2,4)

cout << lru->Get(4)<<endl;  在缓存里寻找key=4的节点,返回该节点的value,输出value=7,该节点已经在最前面了,数据依旧是(4,7)(5,6)(2,4)

cout << lru->Get(1)<< endl;  在缓存里寻找key=1的节点,返回该节点的value,由于节点(1,3)已经被挤出缓存,所以返回为 -1

原文地址:https://www.cnblogs.com/victorywr/p/13334763.html