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
====================2019.01.18==================
逻辑要清晰。更新频率map,再更新双向链表。
class LFUCache {
class Node{
int key;
int val;
int fre;
Node pre;
Node next;
Node(int key,int val,int fre){
this.key=key;
this.val=val;
this.fre=fre;
}
}
HashMap<Integer,Node> fmap;
HashMap<Integer,Node> map;
Node first;
Node end;
final int K;
int cap;
public LFUCache(int capacity) {
map=new HashMap<>();
fmap=new HashMap<>();
first=new Node(0,0,0);
end=new Node(0,0,0);
first.next=end;
end.pre=first;
K=capacity;
}
public int get(int key) {
if(map.get(key)==null) return -1;
Node node=map.get(key);
int fre=node.fre;
node.fre++;
Node n=fmap.get(fre);//插入点
//更新fmap
if(fmap.get(fre)==node){
if(node.pre.fre==fre) fmap.put(fre,node.pre);
else fmap.remove(fre);
}
if(fmap.get(fre+1)!=null){
n=fmap.get(fre+1);
}
fmap.put(fre+1,node);
//更新双向链表
if(node!=n){
node.pre.next=node.next;
node.next.pre=node.pre;
node.next=n.next;
node.pre=n;
n.next.pre=node;
n.next=node;
}
return node.val;
}
public void put(int key, int value) {
if(K==0) return;
if(map.get(key)!=null){
map.get(key).val=value;
get(key);
}else{
Node node=new Node(key,value,1);
//delete
if(cap==K){
Node n=first.next;
if(fmap.get(n.fre)==n) fmap.remove(n.fre);
map.remove(n.key);
first.next=n.next;
n.next.pre=first;
}else{
cap++;
}
//insert
if(fmap.get(1)==null){
node.pre=first;
node.next=first.next;
first.next.pre=node;
first.next=node;
}else{
Node n=fmap.get(1);
node.pre=n;
node.next=n.next;
n.next.pre=node;
n.next=node;
}
fmap.put(1,node);
map.put(key,node);
}
}
}
===============================================
总结:此题要注意的细节太多,借鉴了LRU那题,不同的是多用了了个HashMap保存了频率和该频率下最近用到的key。此题也暴露自己不够仔细的缺点,就是脑子里无法全面的设想各种情况。
class LFUCache {//找到频率最低的?只有是链表了,因为能快速找到下一个节点。。节点封装fre,value,最低频率
HashMap<Integer,Node> fremap;//fre,node:每个频率最后一个节点,便于找到该插入的位置
HashMap<Integer,Node> map;//key,node快速找到当前节点
final private int capacity;
int count=0;
Node start=new Node(0,0,0);//链表起始节点
class Node{
int key;
int val;
int fre;
Node next;
Node pre;
Node(int key,int val,int fre){
this.key=key;
this.val=val;
this.fre=fre;
}
}
public LFUCache(int capacity) {
this.capacity=capacity;
fremap=new HashMap<>();
map=new HashMap<>();
}
public int get(int key) {
if(capacity==0) return -1;
//1.找得到
//更新频率,并插入到链表合适位置,更新频率表
//2.找不到
//返回-1
Node node=map.get(key);
if(node!=null){//考虑起始节点,所以这里简化处理,创建起始节点用作标记节点
Node last=fremap.get(node.fre);
if(last.pre.fre!=last.fre){//该频率只有一个元素,就是当前元素
//更新频率表
fremap.remove(node.fre);
}else if(last==node){//更新频率表!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!相等的时候更新!!!
fremap.put(node.fre,last.pre);
}
if(fremap.get(node.fre+1)!=null){
//原位置
node.pre.next=node.next;
node.next.pre=node.pre;
//插入位置
if(fremap.get(node.fre+1).next!=null) fremap.get(node.fre+1).next.pre=node;
node.next=fremap.get(node.fre+1).next;
node.pre=fremap.get(node.fre+1);
fremap.get(node.fre+1).next=node;
}else if(last!=node){//可能前面已清空或者
node.pre.next=node.next;
node.next.pre=node.pre;
//插入位置
if(last.next!=null) last.next.pre=node;
node.next=last.next;
node.pre=last;
last.next=node;
}
//更新频率表
node.fre++;
fremap.put(node.fre,node);
}else{
return -1;
}
return node.val;
}
public void put(int key, int value) {
Node n=start;
if(capacity==0) return;
if(map.get(key)!=null){
map.get(key).val=value;
get(key);//为了更新
return;
}
//插入位置
Node node=new Node(key,value,1);
map.put(key,node);
//超出容量
if(++count>capacity){
//更新频率表
if(fremap.get(start.next.fre)==start.next){
fremap.remove(start.next.fre);
}
map.remove(start.next.key);
start.next=start.next.next;
if(start.next!=null) start.next.pre=start;
count--;
}
//存在1
if(fremap.get(1)!=null){//当插入新的节点时
if(fremap.get(1).next!=null) fremap.get(1).next.pre=node;
node.next=fremap.get(1).next;
node.pre=fremap.get(1);
fremap.get(1).next=node;
}
else{
if(start.next!=null) start.next.pre=node;
node.next=start.next;
node.pre=start;
start.next=node;
}
//更新频率表
fremap.put(1,node);
}
}