Leetcode 460. LFU Cache

题目链接

题目描述

Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.get(3); // returns 3.
cache.put(4, 4); // evicts key 1.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4

题目分析

主要是页面替换算法中的LFU算法,在一定时期内最少使用的替换掉,与LRU不同的,LFU为每个页面都设置了使用频率,当缓存中容量满的时候,插入新的页面,就从原来的缓存选择一个使用频率最少的页面给替换掉,如果有多哥页面的使用频率相同,那么就选择最早放入缓存的那个。(LFU强调使用频率最少,LRU强调最近是否使用过即可)

例子分析

举个例子来看看,比如说我们的cache的大小为3,然后我们按顺序存入 5,4,5,4,5,7,这时候cache刚好被装满了,因为put进去之前存在的数不会占用额外地方。那么此时我们想再put进去一个8,如果使用LRU算法,应该将4删除,因为4最久未被使用,而如果使用LFU算法,则应该删除7,因为7被使用的次数最少,只使用了一次。LRU只需使用一个Hashmap加一个双向循环链表即可解决, 最近使用的元素放在链表头,最近最少使用的元素放在聊表尾巴。

LFU思路

使用一个HashMap记录(key,value),再使用一个HashMap记录key, frequently,另一个LinkedHashSet记录元素插入
的顺须,设置三个变量,cap记录缓存的大小,val记录缓存中的(key,value), count-(key, 频率),List 记录(频率,key(列表)),key列表中最早加入的元素是在表头的,尾巴是新加入,同一个key<列表中>存放的是使用频率一样的元素,当val.size()==cap的时候,就设法从最小的key<列表中>查找一个key最小元素,然后删除最新加入的即可。

  1. 什么时候需要更新min
    无论什么元素,只要是第一次添加进来的元素,则min一定是等于1的
    如果某个min列表中只有一个元素,并且访问的是min中的元素,则min就要进行更新操作,

参看
刚开始缓存中为空的时候,所有的元素的使用频率为0

import java.util.HashMap;
import java.util.LinkedHashSet;
public class LFUCache {

    private int cap;
    private int min=-1;
    private HashMap<Integer,Integer> value;
    private HashMap<Integer,Integer> frequently;
    private   HashMap<Integer,LinkedHashSet<Integer>> frequently_list;


    LFUCache(int capacity) {
        cap = capacity;
        value = new HashMap<>();
        frequently = new HashMap<>();
        frequently_list =new HashMap<>();
        //此时并没有包含新的元素,只是创建了新的而已
        frequently_list.put(1,new LinkedHashSet<>());
    }

    private void free_space(){
        //空间足够
        if(value.size()<cap)
            return;
        //空间不足够要从min(key)链表中取出一个元素并删除
        int key =(int) frequently_list.get(min).iterator().next();
        System.out.println("evict "+ key);
        /* 三个变量均要删除该变量, */
        value.remove(key);
        frequently.remove(key);
        frequently_list.get(min).remove(key);
    }

    public int get(int key) {
        /* 如果不包含的话 */
        if(!value.containsKey(key)) return -1;
        //如果包含的话,访问一次需要更新一次访问次数
        update(key);
        /* update frequently,and frequently_list;两个东西 */
        return value.get(key);

    }

    private void update(int key){
        int frequency = frequently.get(key);
        //覆盖操作即可
        frequently.put(key,frequency+1);
        //从原来的频率列表中删除,
        frequently_list.get(frequency).remove(key);
        //放进入新的元素列表,如果还没存在相应的频率列表,则应该新创建出来
        if(!frequently_list.containsKey(frequency+1))
            frequently_list.put(frequency+1,new LinkedHashSet<Integer>());
        frequently_list.get(frequency+1).add(key);
        if(frequently_list.get(frequency).size()==0 && frequency==min)
            ++min;
    }

    public void put(int key, int value_) {
        if(value.containsKey(key))
        {
            //执行更新value
            value.put(key,value_);
            //更新frequently 和frequently_list;,
            update(key);
            return;
        }
        //如果是第一次出现的
        //先确保又足够的空间
        free_space();
        value.put(key,value_);
        frequently.put(key,1);
        //第一次出现的情况,即使在这里
        frequently_list.get(1).add(key);
        min = 1;

    }

    public static void main(String[] args) {
        LFUCache cache = new LFUCache(2);

        cache.put(1, 1);
        cache.put(2, 2);
        System.out.println(cache.get(1));       // returns 1
        cache.put(3, 3);    // evicts key 2
        cache.get(2);       // returns -1 (not found)
        System.out.println(cache.get(3));
        cache.put(4, 4);    // evicts key 1.
        System.out.println(cache.get(1));
        System.out.println(cache.get(3));
        System.out.println(cache.get(4));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28350997/article/details/81047502