Leetcode LFU_Cache缓存结构设计

本文介绍了LFU(Least Frequently Used)缓存替换算法的设计,包括如何在缓存满时选择最少使用的记录进行淘汰,并提供了set和get操作的时间复杂度要求。示例展示了具体实现思路,包括使用map存储键值对及其使用次数,以及如何处理冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前LRU最近最久未使用缓存结构设计

采取存储方式为

  • map<key,List<pair<int,int>>::iterator>             存放key到(key,value)映射   可直接get到
  • List<pair<key,value>>                                      存放(key,value)链表  维护大小为k  末尾为最久未使用

 

 

题目描述

一个缓存结构需要实现如下功能。

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;

如果调用次数最少的key有多个,上次调用发生最早的key被删除

这就是LFU缓存替换算法。实现这个结构,K作为参数给出

[要求]

set和get方法的时间复杂度为O(1)

 

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
 

对于每个操作2,输出一个答案

示例1

输入

复制

[[1,1,1],[1,2,2],[1,3,2],[1,2,4],[1,3,5],[2,2],[1,4,4],[2,1]],3

输出

复制

[4,-1]

说明

在执行"1 2 4"后,"1 1 1"被删除。因此第二次询问的答案为-1

 

 

基本设计结构:

即    :

  通过一个min_num记录当前最少set get操作次数

核心map 存放   key到对应(key,value,num)的映射    其中num表示其次数

由于相同的操作数目可能多个(key,value)  故采取拉链法处理map冲突    list链表最后一个表示当前最久的(key,value)

    int capacity;//缓存容量
    int min_num;//最小的set get操作数
    map<int,list<node>::iterator> mp;//存放key对应的(key,value,num)所在的链表位置
    map<int,list<node>> num_node;//存放num次操作数目对应的 (key,value,num)键值对

 

 

核心get方法:

关键点:

通过map找到对应的(key,value,num)

删除拉链  对应节点

 int get(int key)
    {
        
        if(capacity==0)
            return -1;
        if(mp.find(key)==mp.end())
            return -1;//没有该元素
       
        int value=mp[key]->value;//获取到value
        int num=mp[key]->num;
        //同时由于get  将增加get数目
        num_node[num].erase(mp[key]);//删除该key-value
        if(num_node[num].size()==0)  //若删除后为 该拉链Node数量为0 
        {
            num_node.erase(num);
            if(num==min_num)  //如果删除的是最后一个最小值  那么最小值+1
                min_num++; 
        }
        //放入num+1  拉链处   
        num_node[num+1].push_front(node(key,value,num+1));
        mp[key]=num_node[num+1].begin();  
        return value;
    }

 

核心set方法:

 void set(int key,int value)
    {
        if(capacity==0)
            return;
        
        if(mp.find(key)==mp.end())  //之前缓存中不存在
        {
            
            if(mp.size()==capacity) //已经满了  //那么需要删除
            {
                mp.erase(num_node[min_num].back().key);//首先删除在map中的对应的key
                num_node[min_num].pop_back();//list末尾删除该key
                if(num_node[min_num].size()==0)  //如果删除后该被删除
                {
                    num_node.erase(min_num);
                }
              
            }
              
             //新加入到拉链[1]位置
            num_node[1].push_front(node(key,value,1));
            mp[key]=num_node[1].begin();
            min_num=1;//最少set get次数一定是1 
        }
        else  //之前缓存中存在   那么更新key对应的num数量
        {
            int num=mp[key]->num;//该(key,value)之前的操作数量
            num_node[num].erase(mp[key]);
            //在拉链处[num]删除该节点
            if(num_node[num].size()==0)  //被删光了
            {
                num_node.erase(num);
                if(num==min_num)
                    min_num++;  //如果删光的是最少的那一条  那么最少值增加
            }
            
            //同时增加到  [num+1]拉链处 头部
            num_node[num+1].push_front(node(key,value,num+1));
            mp[key]=num_node[num+1].begin();
  
            
        }

    }

 

 

 

完整代码:

struct node
{
    int key,value,num;
    node(int k,int v,int n)
    {
        key=k;
        value=v;
        num=n;
    }

};

class LFU_Cache
{
    private:
    int capacity;//缓存容量
    int min_num;//最小的set get操作数
    map<int,list<node>::iterator> mp;//存放key对应的(key,value,num)所在的链表位置

    map<int,list<node>> num_node;//存放num次操作数目对应的 (key,value,num)键值对
    
    //方便 获取对应操作数目的 键值对  如num_node[1] 将快速定位到只进行一次set get操作的(key,value)
    
    public:
    LFU_Cache(int k)
    {
        capacity=k;  //构造函数
        min_num=0;
    }
    void set(int key,int value)
    {
        if(capacity==0)
            return;
        
        if(mp.find(key)==mp.end())  //之前缓存中不存在
        {
            
            if(mp.size()==capacity) //已经满了  //那么需要删除
            {
                mp.erase(num_node[min_num].back().key);//首先删除在map中的对应的key
                num_node[min_num].pop_back();//list末尾删除该key
                if(num_node[min_num].size()==0)  //如果删除后该被删除
                {
                    num_node.erase(min_num);
                }
              
            }
              
             //新加入到拉链[1]位置
            num_node[1].push_front(node(key,value,1));
            mp[key]=num_node[1].begin();
            min_num=1;//最少set get次数一定是1 
        }
        else  //之前缓存中存在   那么更新key对应的num数量
        {
            int num=mp[key]->num;//该(key,value)之前的操作数量
            num_node[num].erase(mp[key]);
            //在拉链处[num]删除该节点
            if(num_node[num].size()==0)  //被删光了
            {
                num_node.erase(num);
                if(num==min_num)
                    min_num++;  //如果删光的是最少的那一条  那么最少值增加
            }
            
            //同时增加到  [num+1]拉链处 头部
            num_node[num+1].push_front(node(key,value,num+1));
            mp[key]=num_node[num+1].begin();
  
            
        }

    }
    int get(int key)
    {
        
        if(capacity==0)
            return -1;
        if(mp.find(key)==mp.end())
            return -1;//没有该元素
       
        int value=mp[key]->value;//获取到value
        int num=mp[key]->num;
        //同时由于get  将增加get数目
        num_node[num].erase(mp[key]);//删除该key-value
        if(num_node[num].size()==0)  //若删除后为 该拉链Node数量为0 
        {
            num_node.erase(num);
            if(num==min_num)  //如果删除的是最后一个最小值  那么最小值+1
                min_num++; 
        }
        //放入num+1  拉链处   
        num_node[num+1].push_front(node(key,value,num+1));
        mp[key]=num_node[num+1].begin();  
        return value;
    }

};




















class Solution {
public:
    /**
     * lfu design
     * @param operators int整型vector<vector<>> ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LFU(vector<vector<int> >& operators, int k) {
        // write code here
        
        vector<int> res;
        LFU_Cache cache(k);
        for(int i=0;i<operators.size();i++)
        {
            if(operators[i].size()==3 && operators[i][0]==1)
            {
                
                cache.set(operators[i][1], operators[i][2]);
                
            }
            else if(operators[i].size()==2 && operators[i][0]==2)
            {
                res.push_back(cache.get(operators[i][1]));
            }
            
        }
        
        return res;
    }
};

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值