java lfu缓存,缓存算法——LFUCache编码实现(Java版)

LFU算法(Last Frequently Used)

要求:get 和 put 操作, 时间复杂度O(1)

LFU算法的设计原则:

1)put 和 get 操作,都算是使用,freq计数都得加1

2)如果达到容量限制,移除freq最小的那个Cache

3)移除时,如果有多个Cache的freq相同,那么移除最久未使用的那个(LRU)

设计思路:

1)使用 Map 来存储Cache,做到get 的O(1) 时间复杂度。

2)内部使用双链表节点维护缓存队列,列表头部是最久未使用的节点,淘汰时直接淘汰头部节点

3)增加一个Map,存储每一个freq数字的尾节点Node对象引用。

每一次操作 get 和 put 都需要对节点进行移动,具体来讲,是需要把该节点移动到对应freq子列表的尾节点(即最新使用的)这样就做到了get 和 put 操作时,移动节点耗时的 O(1) 时间复杂度。

由于是用的Leetcode的题解直接贴上来,所以Cache就用的是数字,可以改造为泛型。

代码:

public class LFUCache {

/**

* key=key, val=valNode

*/

private Map valMap;

/**

* key=freqNum 也就是调用次数

* val=该次数的队伍尾部node指针

*/

private Map freqNumMap;

/** 双端队列 */

private Node head;

private Node tail;

private int capacity;

public LFUCache(int capacity) {

this.capacity = capacity;

valMap = new HashMap<>(capacity);

freqNumMap = new HashMap<>(capacity);

head = new Node();

tail = new Node();

head.next = tail;

tail.pre= head;

}

public int get(int key) {

Node node = valMap.get(key);

if(node==null) {

return -1;

}

freqNode(node, node.freq+1);

return node.val;

}

public void put(int key, int value) {

Node node = valMap.get(key);

if(node == null){

if(valMap.size() == capacity){

deleteHead();

}

if(valMap.size() < capacity){

addNewNode(key, value);

}

}else if(node.val != value){

node.val = value;

freqNode(node, node.freq + 1);

}

}

/**

* 给node设置freq 并且 根据freq重置在双端列表的位置

* @param node

* @param newFreq

*/

private void freqNode(Node node, int newFreq){

System.out.println("重置Node:" + node + "到freq:" + newFreq);

// 校验修改 freqNumMap 指针

checkAndRemoveFreqTail(node);

// 找到旧freq对应的Node

Node freqNode = findFreqNode(newFreq);

node.freq = newFreq;

insertNode(freqNode, node);

// 添加node到freq

freqNumMap.put(newFreq, node);

}

private Node findFreqNode(int freq){

Node freqNode = freqNumMap.get(freq--);

while(freqNode==null && freq > 0){

freqNode = freqNumMap.get(freq--);

}

return freqNode==null ? head : freqNode;

}

private void addNewNode(int key, int val){

Node newNode = new Node(key, val, 1, null, null);

insertNode(head, newNode);

freqNode(newNode, 1);

valMap.put(key, newNode);

}

private void deleteHead(){

Node firstNode = head.next;

if(firstNode == tail){ // 到头了,删没了

return;

}

// 移除valMap 的值

valMap.remove(firstNode.key);

// 判断freqNumMap, 如果是对应freqNum的尾节点,则更换尾节点

checkAndRemoveFreqTail(firstNode);

// 将节点从列表中移除(连接前后节点, 删除节点引用)

firstNode.pre.next = firstNode.next;

firstNode.next.pre = firstNode.pre;

firstNode.pre=null;

firstNode.next=null;

System.out.println("删除Node" + firstNode);

return;

}

private void insertNode(Node before, Node node){

if(node.pre != null){

node.pre.next = node.next;

}

if(node.next != null){

node.next.pre = node.pre;

}

Node after = before.next;

before.next = node;

after.pre = node;

node.pre = before;

node.next = after;

}

private void checkAndRemoveFreqTail(Node node){

int freq = node.freq;

if(node == freqNumMap.get(freq)){

Node preNode = node.pre;

if(preNode != head && preNode.freq==freq){

freqNumMap.put(freq, preNode);

}else{

freqNumMap.remove(freq);

}

}

}

private static class Node {

int key;

int val;

int freq;

Node next;

Node pre;

@Override

public String toString() {

return "Node{" +

"key=" + key +

", val=" + val +

", freq=" + freq +

'}';

}

public Node() {

}

public Node(int key, int val, int freq, Node pre, Node next) {

this.key = key;

this.val = val;

this.freq = freq;

this.next = next;

this.pre = pre;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值