赞
踩
一个缓存结构需要实现如下功能:
void set(int key,int value):加入或者修改 key 对应的 value
int get(int key):查询 key 对应的 value 值
但是缓存最多放 K 条记录,如果新的 K + 1 条记录需要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。
这个策略为:在缓存结构的 K 条记录中,哪一个 key 从进入缓存结构的时刻开始,被调用 set 或者 get 次数最少,就删掉这个key 的记录;如果调用次数最少的 key 有多个,上次调用发送最早的 key 被删除。
这个就是 LFU 缓存替换算法。实现这个结构,K 作为参数。
解法一:哈希表 + 有序表
缓存中的数据是 k,v 对,所以用 map 存储数据。由于缓存数据需要根据:使用次数和使用时间进行淘汰,如果遍历数据查找需要淘汰的数据,耗时比较高。因此需要维护一个有序结构,方便查找要淘汰的数据。
时间复杂度:O(log K)
空间复杂度:O(K)
import java.util.*; public class LFU1 { public static class Node implements Comparable<Node> { public int key; public int value; // 这个节点发生get或者set的次数总和 public int count; // 最后一次操作的时间 public int time; public Node(int key, int value, int count, int time) { this.key = key; this.value = value; this.count = count; this.time = time; } // 缓存淘汰优先级 // 最少使用(count 越小越容易被淘汰) // count 相同,时间越早越容易被淘汰(time 越小越容易被淘汰) @Override public int compareTo(Node o) { return count == o.count ? Integer.compare(time, o.time) : Integer.compare(count, o.count); } @Override public String toString() { return "Node{" + "key=" + key + ", value=" + value + ", count=" + count + ", time=" + time + '}'; } } public static class LFUCache { // 缓存过期优先级 TreeSet<Node> set = new TreeSet<>(); Map<Integer, Node> map = new HashMap<>(); int capacity; int time = 0; // 用来计算缓存时间 public LFUCache(int capacity) { this.capacity = Math.max(capacity, 0); } public Integer get(int key) { if (!map.containsKey(key)) { return null; } Node node = map.get(key); set(key, node.value); return node.value; } public void set(int key, int value) { this.time += 1; // 更新 if (map.containsKey(key)) { Node node = map.get(key); // 删除再插入(node 的count 和 time 变化了,TreeSet 认为是不同的数据) set.remove(node); node.time = this.time; node.count++; node.value = value; set.add(node); map.put(key, node); return; } // 新增 // 如果内存满了,淘汰一条旧数据 if (this.capacity == this.map.size()) { remove(); } Node node = new Node(key, value, 1, this.time); map.put(key, node); set.add(node); } public void remove() { if (map.size() == 0) { return; } Node node = set.first(); map.remove(node.key); set.remove(node); } } }
解法二:哈希表 + 小顶堆
将有序表更换为小顶堆。
删除数据时,heap.pop()
更新数据后,堆化:heap.heapify(node)。更新数据使得 time 和 count 变大,因此只需要从 node 节点开始向下堆化。
时间复杂度:O(log K)
空间复杂度:O(K)
import java.util.*;
public class LFU3 {
public static class Node {
public int key;
public int value;
// 这个节点发生get或者set的次数总和
public int count;
// 最后一次操作的时间
public int time;
public Node(int key, int value, int count, int time
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。