• 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

  • 相关阅读:
    [Swift]LeetCode843. 猜猜这个单词 | Guess the Word
    [Swift]LeetCode852. 山脉数组的峰顶索引 | Peak Index in a Mountain Array
    [Swift]LeetCode867. 转置矩阵 | Transpose Matrix
    [Swift]LeetCode859. 亲密字符串 | Buddy Strings
    [Swift]LeetCode844. 比较含退格的字符串 | Backspace String Compare
    [Swift]LeetCode824. 山羊拉丁文 | Goat Latin
    PHP 分析1
    php 分析
    停下库
    常用sql
  • 原文地址:https://www.cnblogs.com/victorywr/p/13334763.html
Copyright © 2020-2023  润新知