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.
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?
思路: 这题是LRU的升级版,这里仍然需要一个hashmap<Key, Node> 用来用key来找node,比LRU多的部分就是需要一个frequency map,同时 key 是fre,value是个list,这个list跟LRU的list一模一样,只不过是每一个fre都有这么一个list,所以需要写一个DList的类,来加node,减node,remove last node。Time: O(1), Space: O(N)
Fre, DList<> 这个DList就是每个频率所维护的Least Recent used list;
class LFUCache {
public class Node {
public int key;
public int value;
public int fre;
public Node next;
public Node pre;
public Node(int key, int value) {
this.key = key;
this.value = value;
this.next = null;
this.pre = null;
this.fre = 1;
}
}
public class DList {
public int size;
public Node head;
public Node tail;
public DList() {
this.size = 0;
this.head = new Node(-1, -1);
this.tail = new Node(-1, -1);
head.next = tail;
tail.pre = head;
}
// moveToHead;
public void addNode(Node node) {
Node headnext = head.next;
head.next = node;
node.next = headnext;
headnext.pre = node;
node.pre = head;
this.size++;
}
public Node DeleteLastNode() {
if(size == 0) {
return null;
}
Node last = tail.pre;
last.pre.next = tail;
tail.pre = last.pre;
this.size--;
return last;
}
public void removeNode(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
this.size--;
}
}
public int capacity;
public int minfre;
// key, node
public HashMap<Integer, Node> hashmap;
// fre, LR DList
public HashMap<Integer, DList> fremap;
public LFUCache(int capacity) {
this.capacity = capacity;
this.hashmap = new HashMap<>();
this.fremap = new HashMap<>();
}
public int get(int key) {
if(!hashmap.containsKey(key)) {
return -1;
} else {
// contains the key;
Node node = hashmap.get(key);
updateFreMap(node);
return node.value;
}
}
public void updateFreMap(Node node) {
int fre = node.fre;
DList dlist = fremap.get(fre);
dlist.removeNode(node);
if(dlist.size == 0) {
fremap.remove(fre);
if(fre == minfre) {
minfre++;
}
}
// add to new fre DList;
node.fre++;
fremap.putIfAbsent(node.fre, new DList());
fremap.get(node.fre).addNode(node);
}
public void put(int key, int value) {
if(this.capacity == 0) {
return;
}
if(hashmap.containsKey(key)) {
Node node = hashmap.get(key);
node.value = value;
updateFreMap(node);
} else {
// not contains;
if(hashmap.keySet().size() < this.capacity) {
Node node = new Node(key, value);
minfre = 1;
fremap.putIfAbsent(minfre, new DList());
fremap.get(minfre).addNode(node);
hashmap.put(key, node);
} else {
// evict last node;
DList dlist = fremap.get(minfre);
Node lastnode = dlist.DeleteLastNode();
hashmap.remove(lastnode.key);
// add new node;
minfre = 1;
Node node = new Node(key, value);
fremap.putIfAbsent(minfre, new DList());
fremap.get(minfre).addNode(node);
hashmap.put(key, node);
}
}
}
}
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache obj = new LFUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/