LFU(Least Frequently Used)算法,即最不经常使用算法,是置换算法(也叫淘汰算法)的一种,最早是在学习操作系统时接触的。
原理
当内存不足时,选择淘汰访问次数最少的页面。
实现方法
为每一个页面设置一个访问次数计数器。当一个页面刚被移到内存时,对应的计数器初始化为0;当一个页面被访问时,对应的计数器值加1;当需要淘汰时,取计数器值最小的页面,如果存在多个访问次数最少的页面,淘汰最近最久未使用的页面。
LeetCode上实现LFU算法的题目:460. LFU 缓存。
主要使用两个Map:cache用来存K, Node,Node中有K, V和使用频率;frequencyMap用来存使用频率对应的节点链表,该节点链表使用LinkedHashSet双向链表来存Node。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| public class LFUCache { private Map<Integer, Node> cache; private Map<Integer, LinkedHashSet<Node>> frequencyMap; private int capacity; private int size; private int minFrequency;
public LFUCache(int capacity) { this.capacity = capacity; cache = new HashMap<>(this.capacity); frequencyMap = new HashMap<>(); }
public int get(int key) { Node node = cache.get(key); if (node == null) { return -1; } node.frequency++; addNodeToNewList(node); return node.value; }
public void put(int key, int value) { if (capacity == 0) { return; } Node node = cache.get(key); if (node == null) { node = new Node(key, value); if (size == capacity) { removeEldestNode(); } cache.put(key, node); minFrequency = 0; size++; } else { node.value = value; node.frequency++; } addNodeToNewList(node); }
private void addNodeToNewList(Node node) { if (node.frequency > 0) { removeNodeOnOldList(node); } LinkedHashSet<Node> frequencyList = frequencyMap.get(node.frequency); if (frequencyList == null) { frequencyList = new LinkedHashSet<>(); frequencyMap.put(node.frequency, frequencyList); } frequencyList.add(node); }
private void removeEldestNode() { LinkedHashSet<Node> minFrequencyList = frequencyMap.get(minFrequency); Node eldestNode = minFrequencyList.iterator().next(); if (minFrequencyList.size() == 1) { frequencyMap.remove(minFrequency); } else { minFrequencyList.remove(eldestNode); } cache.remove(eldestNode.key); size--; }
private void removeNodeOnOldList(Node node) { int oldFrequency = node.frequency - 1; LinkedHashSet<Node> oldFrequencyList = frequencyMap.get(oldFrequency); if (oldFrequencyList.size() == 1) { frequencyMap.remove(oldFrequency); if (minFrequency == oldFrequency) { minFrequency = node.frequency; } } else { oldFrequencyList.remove(node); } }
private class Node { private int key; private int value; private int frequency;
public Node(int key, int value) { this.key = key; this.value = value; } } }
|
缺点
该算法的不足之处是:以前曾经多次使用的页面,虽然目前不用也会留在内存,而刚刚移入内存的页面因其访问次数很少反而会被淘汰。