LRU缓存算法设计——哈希双端链表

原题在这里

  题意:实现满足O(1)时间复杂度的get和put操作的LRU算法。

analyse:

  1.第一次做的时候,选用了图论中链式前向星+set哈希处理的解法

    思路:翻译为图,也就是找num时间戳在capacity范围内的边

      对于每一个key,就当作一个节点,每指向一个value就是一条边

      get操作:

        head[key]==0:

          return -1

        head[key]!=0:

           找到对应的num时间戳,哈希set删除,并新建该边。

      put操作:

        如果哈希set中存在时间戳,那么删除

        直接新建边

      建边处理:

        定义数组head,id,mp分别表示key指向num时间戳,num时间戳指向key,num时间戳指向value

        哈希set,表示当前LRU存在的时间戳列表。

        添加head、id、mp数组元素和set当前时间戳

          如果set列表数量大于capacity,则情况set列表头时间戳对应的head对应的key值所指向的时间戳

code:

复制代码
class LRUCache
{
    //翻译在链式前向星图中就是,在num时间戳为固定范围内的边
    vector<int> head, id, mp; // key指向num时间戳,num时间戳指向key,num时间戳指向value
    set<int> st;              //存在的num时间戳表
    int num, m;
    void add(int x, int y)
    {
        //如果有,固定删除head[x]所对应的边,但是不需要实际操作
        mp.emplace_back(y);
        head[x] = ++num;
        id.emplace_back(x);
        st.insert(num);
        while (st.size() > m)
        {
            head[id[*st.begin()]] = 0;
            st.erase(st.begin());
        }
    }

public:
    LRUCache(int capacity)
    {
        num = 0, m = capacity;
        head = vector<int>(1e4 + 9);
        mp.clear(), id.clear();
        id.emplace_back(0);
        mp.emplace_back(0);
        st.clear();
    }

    int get(int key)
    {
        if (head[key] != 0)
        {
            int x = head[key], y = mp[x];
            st.erase(x);
            add(key, mp[x]);
            return y;
        }
        return -1;
    }

    void put(int key, int value)
    {
        int x = head[key];
        if (x)
            st.erase(x); //如果有,直接删除后添加
        add(key, value);
    }
};
图式解法
复制代码

  2.标准解法,map哈希+双端链表

    思路:map存key对应node节点,另外定义整数类型size表示链表节点数量。

    get:

      如果没有则-1,有则直接mp[key]->val,并且将该节点置于链表头。

    put:

      如果没有则新建,否则返回val并且置于表头。

      如果数量大于capacity,那么将链表末尾节点删除。

code:

复制代码
class LRUCache
{
    struct Node
    {
        int key, val;
        Node *next, *prev;
        Node() : key(0), val(0), next(nullptr), prev(nullptr){};
        Node(int x, int y) : key(x), val(y), next(nullptr), prev(nullptr){};
    };
    map<int, Node *> mp;
    Node *head, *tail;
    int size, mx;

public:
    LRUCache(int capacity)
    {
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
        size = 0, mx = capacity;
    }
    void add(Node *node)
    {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    void remove(Node *node)
    {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void move(Node *node)
    {
        remove(node);
        add(node);
    }
    Node *Remove()
    {
        Node *node = tail->prev;
        remove(node);
        return node;
    }
    int get(int key)
    {
        if (mp.count(key) == 0)
            return -1;
        Node *node = mp[key];
        move(node);
        return node->val;
    }

    void put(int key, int value)
    {
        if (mp.count(key) == 0)
        {
            Node *node = new Node(key, value);
            mp[key] = node;
            add(node);
            ++size;
            if (size > mx)
            {
                Node *removed = Remove();
                mp.erase(removed->key);
                delete removed;
                --size;
            }
        }
        else
        {
            Node *node = mp[key];
            node->val = value;
            move(node);
        }
    }
};
Map哈希+双端链表
复制代码

  3.优化解,时间戳哈希+双端链表

    思路:引入一个vector和一个整型变量num时间戳,记录key对应的num时间戳。

    限制于本题key数据范围为[0,1e4]。

code:

复制代码
class LRUCache
{
    struct Node
    {
        int key, val;
        Node *next, *prev;
        Node() : key(0), val(0), next(nullptr), prev(nullptr){};
        Node(int x, int y) : key(x), val(y), next(nullptr), prev(nullptr){};
    };
    Node *head, *tail;
    int size, mx;
    vector<Node *> nums;
    vector<int> start; //存入每一个key对应的num
    int num;

public:
    LRUCache(int capacity)
    {
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
        size = 0, mx = capacity;

        start = vector<int>(1e4 + 9, -1);
        num = 0;
    }
    void add(Node *node)
    {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    void remove(Node *node)
    {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void move(Node *node)
    {
        remove(node);
        add(node);
    }
    Node *Remove()
    {
        Node *node = tail->prev;
        remove(node);
        return node;
    }
    int get(int key)
    {
        if (start[key] == -1)
            return -1;
        Node *node = nums[start[key]];
        move(node);
        return node->val;
    }

    void put(int key, int value)
    {
        if (start[key] == -1)
        {
            Node *node = new Node(key, value);
            start[key] = num++;
            nums.emplace_back(node);
            add(node);
            ++size;
            if (size > mx)
            {
                Node *removed = Remove();
                start[removed->key] = -1; //不用真删除nums内节点
                delete removed;
                --size;
            }
        }
        else
        {
            Node *node = nums[start[key]];
            node->val = value;
            move(node);
        }
    }
};
最优解
复制代码

 

【Over

posted @   Renhr  阅读(41)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示