由于新的产品刚开发完,所以有一个闲散的时间看一看算法,于是在LeetCode上面看一看有没有什么题。里面刚好有一道题,就是LFU算法。一个比较简单的,时间复杂度稍微高一点的LRU算法应该是不难的,毕竟只满足LeetCode能够Acceptable。
LFU(Least FrequentlyUsed)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。如果访问的频率一样,那么时间越早的越优先被淘汰。看了一下算法的介绍,于是很快速的写了一个算法的实现:
package demo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class LFUCache {
private Map<String, Node> cache = null;
private Map<Long, List<Node>> countRecord = null;
private int capacity = 0;
private int used = 0;
public LFUCache(int capacity) {
cache = new LinkedHashMap<>(capacity, 0.75f, true);
this.capacity = capacity;
this.countRecord = new HashMap<>();
}
public int get(int key) {
Node node = cache.get(String.valueOf(key));
if (node == null) {
return -1;
}
node.useCount++;
node.lastGetTime = System.nanoTime();
cache.put(String.valueOf(key), node);
List<Node> list = countRecord.get(node.useCount);
if (list == null || list.size() == 0) {
list = new ArrayList<>();
}
list.add(node);
countRecord.put(Long.valueOf(node.useCount), list);
return node.value;
}
public void set(int key, int value) {
used++;
if (cache.get(String.valueOf(key)) != null) {
cache.remove(String.valueOf(key));
used--;
}
Node node = new Node();
node.value = value;
node.useCount = 1;
node.lastGetTime = System.nanoTime();
if (used <= this.capacity) {
cache.put(String.valueOf(key), node);
} else {
removeLast();
if (cache.size() < this.capacity) {
cache.put(String.valueOf(key), node);
}
}
}
// 淘汰最少使用的缓存
private void removeLast() {
int minCount = 0;
long getTime = 0;
int flag = 0;
String waitRemoveKey = null;
Set<Entry<String, Node>> set = this.cache.entrySet();
ArrayList<Entry<String, Node>> list = new ArrayList<>(set);
ListIterator<Entry<String, Node>> itera = list.listIterator();
while (itera.hasNext()) {
Map.Entry<String, Node> entry = itera.next();
flag++;
String key = entry.getKey();
Integer count = entry.getValue().useCount;
long lastGetTime = entry.getValue().lastGetTime;
if (flag == 1) {
minCount = count;
waitRemoveKey = key;
getTime = entry.getValue().lastGetTime;
if (minCount == 1) {
break;
}
}
if (count < minCount) {
minCount = count;
waitRemoveKey = key;
getTime = lastGetTime;
}
if (minCount == count) {
if (getTime > lastGetTime) {
minCount = count;
waitRemoveKey = key;
getTime = lastGetTime;
}
}
}
if (waitRemoveKey != null) {
this.cache.remove(waitRemoveKey);
}
}
public class Node {
public int value;
public int useCount;
public long lastGetTime;
}
public static void main(String[] args) {
LFUCache cache = new LFUCache(2);
cache.set(1, 1);
cache.set(2, 2);
cache.set(3, 3);
System.out.println(cache.get(3));
System.out.println(cache.get(2));
cache.set(4, 1);
System.out.println(cache.get(3));
System.out.println(cache.get(4));
System.out.println(cache.get(2));
}
}复制代码
但是无论如何,总有一些test case 通不过。这个时候我就很郁闷了。
最后才认真的拜读了LFU的相关知识,访问的含义是不管是get的还是set的同一个值都是“访问”,而不只是get才是访问。
参考资料:
blog.csdn.net/yunhua_lee/…
ju.outofmemory.cn/entry/50447