LFU Cache:最不经常使用页置换算法

来源:互联网 发布:js 点击div刷新页面 编辑:程序博客网 时间:2024/05/20 12:51

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 1cache.put(3, 3);    // evicts key 2cache.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 3cache.get(4);       // returns 4
思路:LFU,就是最不经常使用页置换算法。难点:

一:O(1)访问速度,所以要使用hash,形式是<key,val>

二:更新访问频率,而且也要O(1),所以也要使用hash,形式是<key,count>

三:无多余空间时,删除访问频率最低的元素,所以对于每个count也要记录下,形式是<count,linkededhashset>,因为linkedhashset有序,可以对应当访问次数相同时按先后次序剔除(即删除链表开头元素)

四:存在<key,old> -> <key,new>这种情况,所以除了更近=新键值对以外,也要讲访问频率更新


原理都明白,可真正写的时候真的是困难!!!


public class LFUCache {int min = -1;// 表示当前最小的访问频率等级HashMap<Integer, Integer> val = new HashMap<Integer, Integer>();// key,valHashMap<Integer, Integer> counts = new HashMap<Integer, Integer>();// key,countsHashMap<Integer, LinkedHashSet<Integer>> level = new HashMap<Integer, LinkedHashSet<Integer>>();// count,set// 记录每个访问频率等级集下的keyint size = 0;public LFUCache(int capacity) {this.size = capacity;level.put(1, new LinkedHashSet<Integer>());min = 1;//最小值的作用是为了当空间不足时,找到访问频率最低的那个集中最靠前(出现最早)的元素而设置}public int get(int key) {if (!val.containsKey(key)) {// key不存在,返回-1// System.out.println("key不存在");return -1;}int count = (counts.get(key) == null) ? 0 : counts.get(key);// 记录频率// System.out.println(count);int result = val.get(key);// 获取返回值counts.put(key, count + 1);// 记录下新访问频率level.get(count).remove(key);// 剔除原访问等级下,包含的该keyif (!level.containsKey(count + 1)) {// 如果将要更新的访问等级还为未出现过,创建一个新的访问等级集level.put((count + 1), new LinkedHashSet<Integer>());}level.get((count + 1)).add(key);// 更新访问等级集中的keyif (min == count && level.get(count).size() == 0)// 修改最小值{min++;}// System.out.println("get "+key+" :"+val);return result;}public void put(int key, int value) {if (size <= 0)return;// 没有空间if (val.containsKey(key)) {// 仅仅是更新值,包括空间未满与空间刚刚号重慢两种情况,仅需要更新val值与访问次数(相当于一次get)val.put(key, value);get(key);return;}if ((val.size() >= size))// 空间不足,先移除最近访问次数最低的元素{// System.out.println(min);int oldkey = level.get(min).iterator().next();// System.out.println("oldkey:"+oldkey);val.remove(oldkey);counts.remove(oldkey);level.get(min).remove(oldkey);// System.out.println("after remove ,val:"+val);// System.out.println("after remove ,count:"+counts);}// 添加新元素,此时或者一开始就有剩余空间,或者因为空间已满而清理出空间后有剩余空间val.put(key, value);counts.put(key, 1);level.get(1).add(key);min = 1;// System.out.println("put "+key+" :"+val);}        public static void main(String[] args){    StringBuilder sb = new StringBuilder();    //["LFUCache","put","put","get","put","get","get","put","get","get","get"]    //[[2],[1,1],[2,2],[1],[3,3],[2],[3],[4,4],[1],[3],[4]]        /*    LFUCache obj = new LFUCache(2);    obj.put(1,1);    obj.put(2,2);    sb.append(obj.get(1));    obj.put(3,3);    sb.append(obj.get(2));    sb.append(obj.get(3));    obj.put(4,4);    sb.append(obj.get(1));    sb.append(obj.get(3));    sb.append(obj.get(4));    System.out.println(sb.toString());        */    LFUCache obj = new LFUCache(0);    obj.put(0,0);    sb.append(obj.get(0));    System.out.println(sb.toString());    }}