首页 > 试题广场 >

设计LFU缓存结构

[编程题]设计LFU缓存结构
  • 热度指数:23499 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
一个缓存结构需要实现如下功能。
  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值
但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;
如果调用次数最少的key有多个,上次调用发生最早的key被删除
这就是LFU缓存替换算法。实现这个结构,K作为参数给出

数据范围:
要求:get和set的时间复杂度都是 ,空间复杂度是


若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 4 4"后,"1 1 1"被删除。因此第二次询问的答案为-1   

备注:

public class Solution {
    private int capacity = 0;
    private Map<Integer, Integer> cacheMap = new HashMap<>();
    private Map<Integer, Integer> keyFreqMap = new HashMap<>();
    private Map<Integer, Set<Integer>> freqKeysMap = new TreeMap<>();

    private void set(int key, int value) {
        Integer cacheValue = cacheMap.get(key);
        cacheMap.put(key, value);
        if (cacheValue == null) {
            if (cacheMap.size() > capacity) {
                Map.Entry<Integer, Set<Integer>> minimumFreqEntry =
                    freqKeysMap.entrySet().iterator().next();
                Integer minimumFreqKey = minimumFreqEntry.getValue().iterator().next();
                remove(minimumFreqKey);
            }
        }
        updateFreq(key);
    }

    private int get(int key) {
        Integer cacheValue = cacheMap.get(key);
        if (cacheValue != null) {
            updateFreq(key);
            return cacheValue;
        } else {
            return -1;
        }
    }

    private void remove(int key) {
        cacheMap.remove(key);
        Integer freq = keyFreqMap.remove(key);
        Set<Integer> freqKeys = freqKeysMap.get(freq);
        freqKeys.remove(key);
        if (freqKeys.isEmpty()) {
            freqKeysMap.remove(freq);
        }
    }

    private void updateFreq(int key) {
        Integer freq = keyFreqMap.remove(key);
        if (freq != null) {
            keyFreqMap.put(key, freq + 1);
            Set<Integer> freqKeys = freqKeysMap.get(freq);
            freqKeys.remove(key);
            if (freqKeys.isEmpty()) {
                freqKeysMap.remove(freq);
            }
            freqKeysMap.computeIfAbsent(freq + 1, a -> new LinkedHashSet<>()).add(key);
        } else {
            keyFreqMap.put(key, 1);
            freqKeysMap.computeIfAbsent(1, a -> new LinkedHashSet<>()).add(key);
        }
    }

    public int[] LFU(int[][] operators, int k) {
        capacity = k;
        int resultNum = (int) Arrays.stream(operators).filter(arr -> arr[0] ==
                        2).count();
        int[] resultArray = new int[resultNum];
        for (int i = 0, j = 0; i < operators.length; i++) {
            int[] nums = operators[i];
            int operateType = nums[0];
            if (operateType == 1) {
                set(nums[1], nums[2]);
            } else {
                resultArray[j++] = get(nums[1]);
            }
        }
        return resultArray;
    }
}

编辑于 2024-04-20 22:09:08 回复(0)
    public int[] LFU (int[][] operators, int k) {
        // write code here
        ArrayList<Integer> res=new ArrayList<>();
        Map<Integer,Integer> m1=new LinkedHashMap<>(); // 记录key value
        Map<Integer,Integer> m2=new LinkedHashMap<>(); // 记录key的先后及次数
        for(int i=0;i<operators.length;i++){
            int opt=operators[i][0];
            int key=operators[i][1];
            if(opt==1){
                int val=operators[i][2];
                if(m2.containsKey(key)){ // 写和读都要判断是否已存在
                    int cnt=m2.get(key)+1;
                    m2.remove(key);
                    m1.put(key,val);
                    m2.put(key,cnt);
                }else{
                    if(m1.size()==k){
                        Iterator<Map.Entry<Integer,Integer>> iter=m2.entrySet().iterator();
                        int cnt=Integer.MAX_VALUE ,dk=0;
                        while(iter.hasNext()){
                            Map.Entry<Integer,Integer> entry=iter.next();
                            if(entry.getValue()<cnt){
                                cnt=entry.getValue();
                                dk=entry.getKey();
                            }
                        }
                        m1.remove(dk);
                        m2.remove(dk);
                    }
                    m1.put(key,val);
                    m2.put(key,1);
                }
            }else{
                if(m2.containsKey(key)){
                    int cnt=m2.get(key)+1;
                    m2.remove(key);
                    m2.put(key,cnt);
                }
                res.add(m1.getOrDefault(key,-1));
            }
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }

发表于 2024-03-09 14:18:53 回复(0)
public class Solution {

    private int capacity;

    private Map<Integer, Node> viewCache = new HashMap<>();

    private Map<Integer, Node> cache = new HashMap<>();

    private ArrayList<Integer> result = new ArrayList<>();

    private int minFreq = 1;

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LFU (int[][] operators, int k) {
        this.capacity = k;
        for (int[] operator:operators) {
            handleOpt(operator);
        }
        return listToArray(result);
    }

    private void handleOpt(int[] operator) {
        int optType = operator[0];
        switch (optType) {
            case 1:
                set(operator[1], operator[2]);
                break;
            case 2:
                result.add(get(operator[1]));
                break;
        }
    }

    private int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        Node node = cache.get(key);
        node.freq += 1;
        handleFreq(node);
        return node.value;
    }

    private void set(int key, int value) {
        Node node ;
        if (!cache.containsKey(key)) {
            checkSize();
            node = new Node (key, value);
            cache.put(key, node);
            minFreq = node.freq;
        } else {
            node = cache.get(key);
            node.value = value;
            node.freq += 1;
        }
        handleFreq(node);
    }

    private void handleFreq(Node node) {
        //浏览次数变更,节点去除(完成上下节点的交接,清除与上下节点的指向)
        clearPointer(node);
        //如果存在相同浏览次数的节点,放在节点尾部
        if (viewCache.containsKey(node.freq)) {
            Node head = viewCache.get(node.freq);
            while (head.next != null) {
                head = head.next;
            }
            head.next = node;
            node.pre = head;
            return;
        }
        //不存在这个浏览次数的节点,新建key
        viewCache.put(node.freq, node);
    }

    private void clearPointer(Node node) {
        Node pre = node.pre;
        Node next = node.next;
        if (pre != null) {
            pre.next = next;
        }
        if (next != null) {
            next.pre = pre;
        }
        if (viewCache.get(node.freq - 1) == node) {
            if (next != null) {
                viewCache.put(node.freq - 1, next);
            } else {
                viewCache.remove(node.freq - 1);
                if (node.freq - 1 == minFreq) {
                    minFreq = node.freq;
                }
            }
        }
        node.next = null;
        node.pre = null;
    }

    private void checkSize() {
        if (cache.size() < capacity) {
            return;
        }
        Node node = viewCache.get(minFreq);
        int key = node.key;
        if (node.next != null) {
            node = node.next;
            node.pre = null;
            viewCache.put(minFreq, node);
        } else {
            viewCache.remove(minFreq);
            minFreq++;
        }
        cache.remove(key);
    }

    private int[] listToArray(ArrayList<Integer> list) {
        int[] output = new int[result.size()];
        for (int i = 0; i < list.size(); i++ ) {
            output[i] = list.get(i);
        }
        return output;
    }

    class Node {
        int key;
        int value;
        int freq;
        Node pre;
        Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
            this.freq = 1;
        }
    }

}

发表于 2023-11-09 17:36:22 回复(0)
注释很详细,可以参考
import java.util.*;
//LFU:当缓存满时,哪个key从进入缓存结构时被调用set或者get的次数最少,
//就删掉这个Key的记录,如果调用次数相同则比较时间,删除上次调用发生最早的key
//node 实现Comparable<Node>可以使得treeSet自动使用compareTo来排序
class Node implements Comparable<Node> {
    int key, value, freq, tick;
    Node(int key, int value, int freq, int tick) {
        this.key = key;
        this.value = value;
        this.freq = freq; //使用次数
        this.tick = tick; //最后一次呗访问的时间戳
    }
    //比较两者 用于在TreeSet里排序
    public int compareTo(Node other) {
        //如果被调用次数相同
        if (freq == other.freq) {
            //返回时间差值
            return tick - other.tick;
        }
        //否则返回被调用次数差值
        return freq - other.freq;
    }
}
class LFU {
    private Map<Integer, Node>
    map; //保存节点,用来查询,记录每个键对应的节点。
    private TreeSet<Node>set;//用来记录节点,并按照调用次数和时间戳进行排序
    private int capacity, tick; //capacity:长度
    //初始化
    public LFU(int capacity) {
        this.capacity = capacity;
        this.tick = 0;
        this.map = new HashMap<>();
        this.set = new TreeSet<>();
    }
    //set
    public void set(int key, int value) {
        if (capacity == 0) {
            return;
        }
        //如果map中存在该值则覆盖
        if (map.containsKey(key)) {
            Node node = map.get(key);
            //使用TreeSet
            set.remove(node);
            node.value = value; //更新数据
            node.freq++;//添加使用次数
            node.tick = tick++; //模拟调用时间增加
            set.add(node);//compareTo之后TreeSet根据compareTo返回的结果,为正则将该node放后面(代表更大),负则放前面。删除是从头节点开始删除

        } else {
            //如果长度超过上限
            if (map.size() >= capacity) {
                //根据策略删除节点
                evict();
            }
            Node node = new Node(key, value, 1, tick++); //tick++模拟时间增加
            //再进行插入
            map.put(key, node);
            set.add(node);
        }
    }
    //获取
    public int get(int key) {
        if (capacity == 0 || !map.containsKey(key)) {
            return -1;
        }
        //否则返回获取到的结果
        Node node = map.get(key);
        set.remove(node);
        System.out.println("移除了Node:" + node.key + ":" + node.value);
        node.freq++;//增加调用次数
        node.tick = tick++;//增加调用时间
        set.add(node);//更新位置
        return node.value;
    }
    private void evict() {
        Node node = set.pollFirst(); //检索并删除集合中第一个元素
        map.remove(node.key);//删除
    }
}
//只能有一个Public
public class Solution {

    public int[] LFU (int[][] operators, int k) {
        // write code here
        ArrayList<Integer> data = new
        ArrayList<Integer>();//因为他要返回定长的数组,所以只能用一个别的结构来存储,最后再转成数组


        //先新建一个LFU缓存
        LFU lfu = new LFU(k);
        //遍历二元数组进行对应操作
        for (int[] operator:operators) {
            //判断本次操作并执行
            if (operator[0] == 1) {
                //进行set操作
                lfu.set(operator[1], operator[2]);
            } else {
                //进行get操作 并存入结果数组中
                data.add(lfu.get(operator[1]));
            }
        }
        int [] res = new int[data.size()];
        for (int i = 0; i < data.size(); ++i)
            res[i] = data.get(i);
        return res;
    }
}


发表于 2023-04-15 23:11:12 回复(0)
面向bug编程
import java.util.*;


public class Solution {

    public int[] LFU (int[][] operators, int k) {
        // write code here
        List<Integer> list= new ArrayList<>();
        Map<Integer,Node> map= new HashMap<>();
        Node head=new Node(-1);
        for(int i=0;i<operators.length;i++){
             if(operators[i][0]==1){
                 //说明为set
                 if(map.containsKey(operators[i][1])){
                     //有该节点,需要更新,且次数+1
                     Node node=map.get(operators[i][1]);
                     node.val=operators[i][2];
                     node.time+=1;
                     //节点后移的操作
                     move(node);
                 }else{
                     //没有该节点需要创建,若节点数大于了k,需要删除次数最少的
                     if(map.size()>=k){
                         int key=head.next.key;
                         //达到临界值,需要删除头结点
                         delete(head.next);
                         //更新map
                         map.remove(key);
                     }
                     //新增节点
                     Node newNode=new Node(operators[i][1],operators[i][2],1);
                     Node next=head.next;
                     
                     head.next=newNode;
                     newNode.pre=head;
                     if(next!=null){
                         next.pre=newNode;
                         newNode.next=next;
                         move(newNode);
                     }
                     //更新map
                     map.put(operators[i][1],newNode);
                     
                 }
             }else{
                 //说明为查询,get
                 if(map.containsKey(operators[i][1])){
                     Node node=map.get(operators[i][1]);
                     node.time+=1;
                     list.add(node.val);
                     //节点次数+1,且往后移动的操作
                     move(node);
                 }else{
                     list.add(-1);
                 }
             }
        }
        //处理结果
        int[] res=new int[list.size()];
        for(int i=0;i<list.size();i++){
            res[i]=list.get(i);
        }
        return res;
    }
    
    //移动
    public void move(Node node){
        if(node==null||node.next==null){
            return;
        }
        Node p=node;
        while(!(p.next==null)){
            if(p.time>=p.next.time){
                p=p.next;
            }else{
                if(p!=node){
                     //说明找到目标
                    node=delete(node);
                    Node next=p.next;//下一个节点
                    
                    p.next=node;
                    node.pre=p;
                    node.next=next;
                    next.pre=node;
                }
                return;  
             
            }
        }
        //说明到尾节点
        node=delete(node);
        p.next=node;
        node.pre=p;
    }
    
    //删除指定节点
    public Node delete(Node node){
        if(node.next==null){
            Node pre=node.pre;
            pre.next=null;
            node.pre=null;
        }else{
            //说明后面有节点
            Node pre=node.pre;
            pre.next=pre.next.next;
            pre.next.pre=pre;
            node.next=null;
            node.pre=null;
        }
        return node;
    }
    
    class Node {
        int key;
        int val;
        int time;
        Node next;
        Node pre;
        public Node(int key,int val,int time){
            this.key=key;
            this.val=val;
            this.time=time;
        }
        public Node(){}
        public Node(int val){
            this.val=val;
        }
    }
}


发表于 2022-09-08 22:55:11 回复(0)
import java.util.*;
public class Solution {
    //使用两个哈希表,一个用来存储元素,另一个用来存储元素被调用的次数
    //如果hash1.size()<k ,可以set直接set,如果hash1.size()==k,要先删除count最小的元素,然后再set
     LinkedHashMap<Integer,Integer> hash1=new LinkedHashMap<>(); //key,value
     LinkedHashMap<Integer,Integer> hash2=new LinkedHashMap<>(); //key,count
     private int k;  //容量大小
    public int[] LFU (int[][] operators, int k) {
        this.k=k;
        int resLen=(int)Arrays.stream(operators).filter(x->x[0]==2).count();//提前确定结果数组的长度
        int[] result=new int[resLen]; //对于每个操作2,返回一个答案放进结果数组
        int num=0;
        for(int[] op: operators){
            if(op[0]==1){ //set 
                setEle(op[1],op[2]);
            }else if(op[0]==2){
                 int r= getEle(op[1]);
                result[num++]=r;
            }
        }
        return result;
    }
    public void setEle(int key,int value){
        //往结构里放入元素 ,添加元素,元素会有变化,先判断hash1表里能不能找到key,有的话,就属于更新元素,count+1
        // 如果hash1里没有,就是新元素,判断hash1.size()如果不是<k,就要删除一个元素再添加
        if(hash1.containsKey(key)){ //hash1表里有的,hash2表里一定也有
            hash1.put(key,value);//会覆盖value
            int val=hash2.get(key)+1;
            hash2.remove(key);
            hash2.put(key,val);
            //hash2.put(key,hash2.get(key)+1);
        }else{
            if(hash1.size()==k){
                int delkey=findMin();
                hash1.remove(delkey); //同步删除hash1和hash2表的调用次数最少的元素
                hash2.remove(delkey);
            }
            hash1.put(key,value);
            hash2.put(key,1);
        }
        
    }
    public int getEle(int key){
        //根据传入的key值查找元素.查找元素,不会导致元素有变化,所以就是直接从hash1里找,找不到就返回-1
        if(hash1.containsKey(key)){
            int val=hash2.get(key)+1;
            hash2.remove(key);
            hash2.put(key,val); //这里是利用LinkedHashMap的有序排序特性,每次调用就把key和count放到后面
            return hash1.get(key);
        }else{
            return -1;
        }
        
    }
    public int findMin(){
        int min=Integer.MAX_VALUE;
        int res=0;
        //返回count最小的元素的key值
        Set<Integer> keys=hash2.keySet(); //从LinkedHashMap前到后遍历,最前面的就算最早被调用的记录
        for(int key:keys){        //用来处理删除多个调用次数一样的元素,要删除上次调用发生最早的记录
            int count=hash2.get(key);
            if(count<min){
                min=count;
                res=key;
            }
        }
        return res;
    }
}

发表于 2022-07-19 17:08:57 回复(0)
思路:
为了实现所有操作都是O(1)时间复杂度,只能使用Map,用三个map分别维护值,频率,相同频率的时序(这里的value使用LinkedHashSet);
get 和 put 都要去维护这三个表,抽象出三个函数专门去维护三个表;removeMinFreqKey() 、 increaseFreq() 和 addNew();
- get() 只需要判空,增加访问值的频率就行
- put() 有key,更新value 和 freq+1 (increaseFreq());
          没key,判断容量 --> 满了,删除最小freq的 (removeMinFreqKey()) --> 未满,addNew()
import java.util.*;


public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    Map<Integer, Integer> keyToValue = new HashMap<>();
    Map<Integer, Integer> keyToFreq = new HashMap<>();
    Map<Integer, LinkedHashSet<Integer>> freqToKeys = new HashMap<>();
    int cap = 0;
    int minFreq = 0;
    public int[] LFU (int[][] operators, int k) {
        int m = operators.length;
        this.cap = k;
        if (m == 0) return new int[0];
        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            int opt = operators[i][0];
            int key = operators[i][1];
            if (opt == 1) {
                int val = operators[i][2];
                set(key, val);
            } else if (opt == 2) {
                res.add(get(key));
            }
        }
        return res.stream().mapToInt(i -> i).toArray();
    }
    
    private Integer get(int key) {
        if (!keyToValue.containsKey(key)) {
            return -1;
        }
        increaseFreq(key);
        return keyToValue.get(key);
    }
    
    private void set(int key, int val) {
        // 更新缓存
        if (keyToValue.containsKey(key)) {
            keyToValue.put(key, val);
            increaseFreq(key);
            return;
        }
        
        // LFU删除最小使用频率缓存
        if (keyToValue.size() >= this.cap) {
            removeMinFreq();
        }
        
        // 添加新的缓存
        addNew(key, val);
    }
    
    // 更新缓存使用频率,维护KF、FK两个map
    private void increaseFreq(int key) {
        int freqVal = keyToFreq.get(key);
        // 更新KF表
        keyToFreq.put(key, freqVal + 1);
        // 更新这个key所在FK表中的位置,先删除旧位置
        freqToKeys.get(freqVal).remove(key);
        // 加入FK表新位置
        freqToKeys.putIfAbsent(freqVal + 1, new LinkedHashSet<>()); // 先新建一个映射
        freqToKeys.get(freqVal+1).add(key); // 再插入key
        
        // 判断旧频率的FK表还有val没,没有就清空
        if (freqToKeys.get(freqVal).isEmpty()) {
            freqToKeys.remove(key);
            
            // 如果刚好是最小频率,更新一下minFreq
            if (freqVal == this.minFreq) {
                this.minFreq++;
            }
        }
        
    }
    
    // LFU删除最小使用频率缓存,相同频率删除最久未使用缓存(LFU)维护KF、FK和KV三个map
    private void removeMinFreq() {
        LinkedHashSet<Integer> keyList = freqToKeys.get(minFreq);
        int deleteKey = keyList.iterator().next();
        // 更新FK表
        keyList.remove(deleteKey);
        if (keyList.isEmpty()) {
            freqToKeys.remove(minFreq);
        }
        // 更新KF表
        keyToFreq.remove(deleteKey);
        // 更新KV表
        keyToValue.remove(deleteKey);
    }
    
    // 添加新缓存,维护三个map
    private void addNew(int key, int val) {
        keyToValue.put(key, val);
        keyToFreq.put(key, 1);
        freqToKeys.putIfAbsent(1, new LinkedHashSet<>());
        freqToKeys.get(1).add(key);
        this.minFreq = 1;
    }
}


发表于 2022-06-08 16:46:19 回复(0)
这个题很简单,注意重复的值,移除最早的那个,理解错意思,搞了好久。用了两个map,第二个可以保证set、get的顺序。
    Map<Integer, Integer> valueMap;
    LinkedHashMap<Integer, Integer> countMap;

    public int[] LFU(int[][] operators, int k) {
        // write code here
        int size = 0;
        valueMap = new HashMap<>(k);
        countMap = new LinkedHashMap<>(k);
        ArrayList<Integer> list = new ArrayList<>();
        //校验operators
        int length = operators.length;
        for (int i = 0; i < length; i++) {
            int[] curr = operators[i];
            if (curr[0] == 1) {
                if (!valueMap.containsKey(curr[1]) && size >= k) {
                    int min = Integer.MAX_VALUE, key = min;
                    Set<Map.Entry<Integer, Integer>> entries = countMap.entrySet();
                    for (Map.Entry<Integer, Integer> entry : entries) {
                        if (min > entry.getValue()) {
                            min = entry.getValue();
                            key = entry.getKey();
                        }
                    }
                    Map.Entry<Integer, Integer> first = entries.iterator().next();
                    if (first.getValue() == min){
                        key = first.getKey();
                    }
                    valueMap.remove(key);
                    countMap.remove(key);
                    size--;
                }
                this.set(curr[1], curr[2]);
                size++;
            } else {
                list.add(this.get(curr[1]));
            }
        }
        return list.stream().mapToInt(x -> x).toArray();
    }

    public int get(int x) {
        int value = valueMap.getOrDefault(x, -1);
        if (value != -1) {
            int count = countMap.get(x);
            countMap.remove(x);
            countMap.put(x, count + 1);
        }
        return value;
    }

    public void set(int x, int y) {
        countMap.put(x, countMap.getOrDefault(x, 0) + 1);
        valueMap.put(x, y);
    }

发表于 2022-05-26 21:06:27 回复(0)
仿造LRU终于通过!
import java.util.*;
public class Solution {
    public int[] LFU (int[][] operators, int k) {
        LFUCache cache = new LFUCache(k);
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < operators.length; i++) {
            if (operators[i][0] == 1) {
                cache.set(operators[i][1], operators[i][2]);
            } else if (operators[i][0] == 2) {
                list.add(cache.get(operators[i][1]));
            }
        }
        int[] res = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            res[i] = list.get(i);
        }
        return res;
    }
}

class LFUCache {
    private LinkedHashMap<Integer, Integer> cache;
    private LinkedHashMap<Integer, Integer> useCount;
    private int cap;
    public LFUCache(int cap) {
        cache = new LinkedHashMap<>();
        useCount = new LinkedHashMap<>();
        this.cap = cap;
    }
    void set(int key, int value) {
        if (cache.containsKey(key)) {
            cache.put(key, value);
            makeRecently(useCount, key);
        } else {
            if (cache.size() >= cap) {
                int leastKey = getLeastKey(useCount);
                cache.remove(leastKey);
                useCount.remove(leastKey);
            }
            cache.put(key, value);
            makeRecently(useCount, key);
        }
    }
    int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        makeRecently(useCount, key);
        return cache.get(key);
    }
    // 当key对应的次数相同时,要返回最长时间不用的那个key
    int getLeastKey(LinkedHashMap<Integer, Integer> useCount) {
        int minVal = Integer.MAX_VALUE;
        int key = -1;
        for (Map.Entry<Integer, Integer> entry : useCount.entrySet()) {
            if (minVal > entry.getValue()) {
                minVal = entry.getValue();
                key = entry.getKey();
            }
        }
        return key;
    }
    // 将useCount标记为最近使用
    void makeRecently(LinkedHashMap<Integer, Integer> useCount, int key) {
        int val = useCount.getOrDefault(key, 0);
        useCount.remove(key);
        useCount.put(key, val + 1);
    }
}


发表于 2022-05-20 21:23:20 回复(0)
感觉这题有点复杂

import java.util.*;
public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LFU (int[][] operators, int k) {
        // write code here
        LRUCache lru = new LRUCache(k);
        int len = 0;
        for(int i = 0; i < operators.length; i++){
            if(operators[i][0] == 2)
                len++;
        }
        // 返回数组
        int[] res = new int[len];
        len = 0;
        for(int i = 0; i < operators.length; i++){
            if(operators[i][0] == 1){
                lru.set(operators[i][1],operators[i][2]);
            }else{
                res[len++] = lru.get(operators[i][1]);
            }
        }
        return res;
    }
}
class LRUCache{
    int capacity;
    HashMap<Integer,Integer> kv;
    HashMap<Integer,Integer> kt;
    HashMap<Integer,LinkedHashSet<Integer>> tk;
    int minTimes;

    public LRUCache(int capacity){
        this.kv= new HashMap<>();
        this.kt= new HashMap<>();
        this.tk= new HashMap<>();
        this.minTimes= 0; //刚开始的次数为0
        this.capacity= capacity;
    }

    public int get(int key){
        if(!kv.containsKey(key)){
            return -1;
        }
        // 该key访问次数要加1
        increaseTimes(key);
        return kv.get(key);
    }


    public void set(int key,int value){
        // 容量不合法
        if(this.capacity <= 0)
            return;

        /* key存在 */
        // 1.覆盖value
        if(kv.containsKey(key)){
            kv.put(key,value);
            //2.访问次数+1
            increaseTimes(key);
            return;
        }

        /* key不存在 */
        // 1、cache满了
        if(this.capacity <= kv.size()){
            // 淘汰Times最小的
            removeMinTimes();
        }

        // 2、cache没满
        // 先插入kv表
        kv.put(key,value);
        // kt表 首次插入,访问次数为1
        kt.put(key,1);
        // 插入tk表 
        // 当times为1的所有的key的集合不存在时,就新建一个
        tk.putIfAbsent(1,new LinkedHashSet<Integer>());
        // 否则,则直接插入
        tk.get(1).add(key);
        // 插入后此时最小的times肯定就是1了
        this.minTimes = 1;
    }


    private void increaseTimes(int key){
        // 拿到原来的times
        int times = kt.get(key);
        // 更新kt表
        kt.put(key,times+1);

        // 更新tk表
        // 将key从原来的times,set中去除
        tk.get(times).remove(key);
        // 将key加入到times+1,set中去
        // 但是还需要考虑times+1对应的set集合是否存在
        tk.putIfAbsent(times+1,new LinkedHashSet<Integer>());
        tk.get(times+1).add(key);
        // 此时基本已经完成了,但是我们还需要做一些细节处理
        // 当我们从原来的set集合中移除的时候,我们得考虑一下这个key是否为其最后一个
        if(tk.get(times).isEmpty()){
            // 如果是的话,则我们需要销毁对应的set集合
            tk.remove(times);
            // 如果times刚好为最小的,则需要更新minTimes
            if(times == this.minTimes){
                this.minTimes++;
            }
        }

    }

    private void removeMinTimes(){
        // 获取最小的times的set集合
        LinkedHashSet<Integer> set = tk.get(this.minTimes);

        // 这里是直接将两个步骤结合起来,利用了LinkedHashSet这个数据结构底层的设计
        // LinkedHashSet底层是按照key放进去的时间先后进行排序的
        // 所以此时set集合中的第一个即为需要删除的key
        int delKey = set.iterator().next();

        // 移除掉
        set.remove(delKey);
        if(set.isEmpty()){
            tk.remove(this.minTimes);
        }
        // 更新kv表
        kv.remove(delKey);
        // 更新tk表
        tk.remove(delKey);

    }
}


发表于 2022-02-10 15:32:43 回复(0)
java 使用双Map实现 77%
import java.util.*;


public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public static int[] LFU (int[][] operators, int k) {
        // write code here
        // 缓存k条记录
        Map<Integer, Integer> cache = new LinkedHashMap<Integer, Integer>();
        // 记录该key被调用的次数
        Map<Integer, Integer> freq = new LinkedHashMap<Integer, Integer>();
        List<Integer> ans = new ArrayList<Integer>();
        for(int[] ops : operators){
            int op = ops[0];
            if(op == 1){  // 执行插入操作
                if(cache.size() == k){
                    // 删除记录
                    int min = Integer.MAX_VALUE, removeKey = -1;
                    for(Map.Entry<Integer,Integer> entry : freq.entrySet()){
                        int tempMin = entry.getValue();
                        if(tempMin < min){
                            removeKey = entry.getKey();
                            min = tempMin;
                        }
                    }
                    // 删除该记录
                    cache.remove(removeKey);
                    freq.remove(removeKey);
                }
                cache.put(ops[1], ops[2]);
                freq.put(ops[1], freq.getOrDefault(ops[1],0)+1);
            }else if(op == 2){ // 执行获取操作
                int temp = cache.getOrDefault(ops[1],-1);
                ans.add(temp);
                if(freq.containsKey(ops[1])) freq.put(ops[1], freq.get(ops[1])+1);
            }
        }
        return ans.stream().mapToInt(Integer::intValue).toArray();
    }
}


发表于 2021-11-16 11:22:13 回复(0)
import java.util.*;


public class Solution {
   
    public int[] LFU (int[][] operators, int k) {
        // hashMap和priorityQueue保存,hashMap存调用次数和计时,priorityQueue排序
        HashMap<Integer,Node> count=new HashMap<>();//对key计数,key=operators[i][1],value=node node.count计数 node.time 计时
        HashMap<Integer,int[]> temp=new HashMap<>(); //保存key(operators[i][1])和operators[i]的映射,好方便找出value值
        PriorityQueue<Integer> minHeap=new PriorityQueue<>((o1,o2)->count.get(o1).count.equals(count.get(o2).count)?
            count.get(o1).time.compareTo(count.get(o2).time):count.get(o1).count.compareTo(count.get(o2).count));//优先队列,取count值较小的,count值一致时取time小的
        ArrayList<Integer> res=new ArrayList<>();
        int time=0;
        for(int i=0;i<operators.length;i++){
            if(operators[i][0]==1){
                if(temp.size()==k){
                    int poll=minHeap.poll(); //把调用次数最少的poll掉
                    count.remove(poll);
                    temp.remove(poll);
                }
                //插入temp和count
                temp.put(operators[i][1],operators[i]);
                if(count.containsKey(operators[i][1])){
                    Node node=new Node(count.get(operators[i][1]).count+1,time++);
                    count.put(operators[i][1],node);
                    minHeap.remove(operators[i][1]);
                    minHeap.offer(operators[i][1]);
                }else{
                    Node node1=new Node(1,time++);
                    count.put(operators[i][1],node1);
                    minHeap.offer(operators[i][1]);
                }
            }else if(operators[i][0]==2){
                if(temp.get(operators[i][1])==null){
                    res.add(-1);
                }else{
                    int[] operator=temp.get(operators[i][1]);
                    res.add(operator[2]);
                    if(count.containsKey(operators[i][1])){
                      Node node2=new Node(count.get(operators[i][1]).count+1,time++);
                    count.put(operators[i][1],node2);
                         minHeap.remove(operators[i][1]);
                        minHeap.offer(operators[i][1]);
                    }else{  
                        Node node3=new Node(1,time++);
                        count.put(operators[i][1],node3);
                        minHeap.offer(operators[i][1]);
                    }
                    
                }
            }
        }
        int[] result=new int[res.size()];
        for(int i=0;i<res.size();i++){
            result[i]=res.get(i);
        }
        return result;
    }
}

class Node{
    public Integer count;
    public Integer time;
    
    public Node(Integer count,Integer time){
        this.count=count;
        this.time=time;
    }
}


编辑于 2021-04-14 12:30:06 回复(0)
import java.util.*;
public class Solution {
    public int[] LFU (int[][] operators, int k) {
        // write code here
        LinkedHashMap<Integer,Integer> map=new LinkedHashMap<>();
        LinkedHashMap<Integer,Integer> num=new LinkedHashMap<>();
        ArrayList<Integer> list=new ArrayList<>();
            
        for(int[] i:operators)
        {
            int op=i[0],key=i[1];
                
            if(op==1)
            {
                if(map.size()==k)
                {   
                    int leastnumkey=(int)getMinKey(num);
                    map.remove(leastnumkey);
                    num.remove(leastnumkey);
                }                                         //若满,则移除表中最不常用的
                int count=0;
                map.put(key,i[2]);                       //未满,添加或者覆盖
                num.put(key,count);
            }
            if(op==2)
            {
                if(map.containsKey(key))
                {
                          int count=num.get(key);
                          num.put(key,count+1);//覆盖
                          int val=map.remove(key);
                          map.put(key,val);//不要忘记将改key设为最常用的
                          list.add(val);//弹出到list
                }
                else
                    list.add(-1);
            }
                
        }
        int[] arr=new int[list.size()];//将arrylist变为数组形式返回
        for(int i=0;i<arr.length;i++)
            arr[i]=list.get(i);
        return arr;
    }
        
//     public static Object getMinKey(LinkedHashMap<Integer, Integer> map) {
//         if (map == null) return null;
//         Set<Integer> set = map.keySet();
//         Object[] obj = set.toArray();
//         //Arrays.sort(obj);
//         return obj[0];//(此方法太大,运行超时)
//          }
   public static Object getMinKey(Map<Integer, Integer> map) {
        int minkey = 0,  min = Integer.MAX_VALUE;
        for(Map.Entry<Integer, Integer> entry: map.entrySet()) {
            if(entry.getValue() < min) {
                min = entry.getValue();
                minkey = entry.getKey();
            }
        }
        return minkey;
    }
    
}
//方法2:
import java.util.*;


public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LFU (int[][] operators, int k) {
        // write code here
        Map<Integer, Integer[]> map = new LinkedHashMap<>(k);
        List<Integer> res = new ArrayList<>();
        for(int [] ops: operators) {
            if(ops[0] == 1) {
                // set
                set(map, ops[1], ops[2], k);
            }else if(ops[0] == 2) {
                // get
                res.add(get(map, ops[1]));
            }
        }
         int[] arr=new int[res.size()];//将arrylist变为数组形式返回
        for(int i=0;i<arr.length;i++)
            arr[i]=res.get(i);
        return arr;
    }
    
    // set
    private void set(Map<Integer, Integer[]> map, int key, int val, int cap) {
        if(map.size() == cap) {
            remove(map);
        }
            map.put(key, new Integer[]{val, 1});
        
    }
    
    // get
    private Integer get(Map<Integer, Integer[]> map, int key) {
        Integer[] value = map.get(key);
        if(value == null) {
            return -1;
        }else {
            map.put(key, new Integer[]{value[0], value[1] + 1});
            return value[0];
        }
    }
    
    // remove
    private void remove(Map<Integer, Integer[]> map) {
        int key = 0,  min = Integer.MAX_VALUE;
        for(Map.Entry<Integer, Integer[]> entry: map.entrySet()) {
            if(entry.getValue()[1] < min) {
                min = entry.getValue()[1];
                key = entry.getKey();
            }
        }
        map.remove(key);
    }
}
编辑于 2021-04-05 17:39:19 回复(0)
import java.util.*;


public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    Map<Integer,Integer[]>map=new LinkedHashMap<>();
    public int[] LFU (int[][] operators, int k) {
        List<Integer>res=new ArrayList<>();
        for(int[]op:operators){
            if(op[0]==1){//set(x,y)
                set(op[1],op[2],k);
            }
            else if(op[0]==2){//get(x)
               res.add(get(op[1]));
            }
            
        }
        int n=res.size();
        int[]result=new int[n];
        for(int i=0;i<n;i++) result[i]=res.get(i);
        return result;
       
    }
    public int get(int key){
        if(map.containsKey(key)) {
            Integer[]val=map.get(key);
            map.put(key,new Integer[]{val[0],val[1]+1});
            return val[0];
        }
        else return -1;
    }
    public void set(int key,int value,int cap){
        if(map.size()==cap){
            removeMin();
        }
        
        if(map.containsValue(key)){
            Integer[]arr=map.get(key);
            map.put(key,new Integer[]{value,arr[1]+1});//次数+1
        }
        else map.put(key,new Integer[]{value,1});
        
    }
    public void removeMin(){//删除map中time最小的元素
        int minKey=0,minTime=Integer.MAX_VALUE;
        for(Integer key:map.keySet()){
            Integer[]arr=map.get(key);
            if(arr[1]<minTime){
                minTime=arr[1];
                minKey=key;
            }
        }
        map.remove(minKey);
    }

}

发表于 2021-03-25 20:44:37 回复(0)

问题信息

难度:
19条回答 3725浏览

热门推荐

通过挑战的用户

查看代码