Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.java
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.算法
Note that the number of times an item is used is the number of calls to the get and put functions for that item since it was inserted. This number is set to zero when the item is removed.緩存
Follow up:
Could you do both operations in O(1) time complexity?性能
Example:this
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(Least Frequently Used 最近使用次數最少原則)。設計
簡單介紹一下LFU。LFU是指put新的元素時,一旦當前緩存的數量達到上限,就按照最近訪問次數從低到高開始淘汰緩存中現有的元素。put和get操做都會增長訪問次數。若是淘汰時出現多個訪問次數相同的key時,則按照LRU來操做,即優先淘汰最近一次訪問間隔最遠的元素。code
根據上面的思路咱們知道,在設計這個緩存的時候,咱們一共須要記錄三個信息:隊列
簡單介紹一下LinkedHashSet。LinkedHashSet首先知足Set的基本要求,即位於該集合中的元素不會重複。其次,它會記錄元素加入集合中的順序,底層的話實際上是經過鏈表來存儲,所以加入元素至鏈表末端或者是從鏈表頭取出元素的性能成本都很低。ci
代碼以下:rem
HashMap<Integer, Integer> vals; HashMap<Integer, Integer> counts; HashMap<Integer, LinkedHashSet<Integer>> lists; int cap; int min = -1; public LFUCache(int capacity) { cap = capacity; vals = new HashMap<>(); counts = new HashMap<>(); lists = new HashMap<>(); lists.put(1, new LinkedHashSet<>()); } public int get(int key) { if(!vals.containsKey(key)) { return -1; } int count = counts.get(key); counts.put(key, count+1); lists.get(count).remove(key); if(count==min && lists.get(count).size()==0) { min++; } if(!lists.containsKey(count+1)) { lists.put(count + 1, new LinkedHashSet<>()); } lists.get(count+1).add(key); return vals.get(key); } public void put(int key, int value) { if(cap<=0) { return; } if(vals.containsKey(key)) { vals.put(key, value); get(key); return; } if(vals.size() >= cap) { int evit = lists.get(min).iterator().next(); lists.get(min).remove(evit); vals.remove(evit); } vals.put(key, value); counts.put(key, 1); min = 1; lists.get(1).add(key); }
這裏經過min這個值記錄當前的最低訪問次數,從而在執行淘汰算法的時候能夠直接從對應訪問次數的隊列中直接刪除。