来源力扣第460题LFU缓存
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。它应该支持以下操作:get
和 put
。
get(key)
- 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value)
- 如果键不存在,请设置或插入值。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近 最少使用的键。
「项的使用次数」就是自插入该项以来对其调用 get
和 put
函数的次数之和。使用次数会在对应项被移除后置为 0 。
两个Map实现
利用一个Map存放key到value值的映射,一个Map存放key到freq的映射,由于题目要求当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近 最少使用的键
,因而采用linkedHashMap实现平局,默认是插入顺序,true为访问顺序
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
| class LFUCache{
private int capacity;
private HashMap<Integer,Integer> map1; private HashMap<Integer,Integer> map2;
public LFUCache(int capacity) { this.capacity = capacity; map1 = new HashMap<>(capacity); map2 = new LinkedHashMap<>(capacity,0.75F,true); }
public int get(int key) { if(map1.containsKey(key)){ map2.put(key,map2.get(key)+1); return map1.get(key); } return -1; }
public void put(int key, int value) { if(capacity == 0) return; if(map1.containsKey(key)){ map1.put(key,value); map2.put(key,map2.get(key)+1); }else{ if(map1.size() == capacity){ int min = Integer.MAX_VALUE; int temp = key; for(Map.Entry<Integer,Integer> entry : map2.entrySet()){ if(min > entry.getValue()){ temp = entry.getKey(); min = entry.getValue(); } } map1.remove(temp); map2.remove(temp); } map1.put(key,value); map2.put(key,1); } }
}
|
双向链表
来自sweetiee
HashMap cache
存缓存的内容; 将写法 1 写法 2 中的 freqMap
不再用 HashMap
来表示,而是直接用双向链表 DoubleLinkedList firstLinkedList; DoubleLinkedList lastLinkedList
,省去了一些哈希相关的耗时,也不需要用 min 来存储最小频次了,lastLinkedList.pre
这条 DoubleLinkedList
即为最小频次对应的 Node 双向链表,lastLinkedList.pre.tail.pre
这个 Node 即为最小频次的双向链表中的所有 Node 中最先访问的 Node,即容量满了后要删除的 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| class LFUCache {
Map<Integer, Node> cache;
DoubleLinkedList firstLinkedList; DoubleLinkedList lastLinkedList;
int size; int capacity;
public LFUCache(int capacity) { cache = new HashMap<> (capacity); firstLinkedList = new DoubleLinkedList(); lastLinkedList = new DoubleLinkedList(); firstLinkedList.post = lastLinkedList; lastLinkedList.pre = firstLinkedList; this.capacity = capacity; }
public int get(int key) { Node node = cache.get(key); if (node == null) { return -1; }
freqInc(node); return node.value; }
public void put(int key, int value) { if (capacity == 0) { return; }
Node node = cache.get(key); if (node != null) { node.value = value; freqInc(node); } else { if (size == capacity) { cache.remove(lastLinkedList.pre.tail.pre.key); lastLinkedList.removeNode(lastLinkedList.pre.tail.pre); size--; if (lastLinkedList.pre.head.post == lastLinkedList.pre.tail) { removeDoubleLinkedList(lastLinkedList.pre); } } Node newNode = new Node(key, value); cache.put(key, newNode); if (lastLinkedList.pre.freq != 1) { DoubleLinkedList newDoublyLinedList = new DoubleLinkedList(1); addDoubleLinkedList(newDoublyLinedList, lastLinkedList.pre); newDoublyLinedList.addNode(newNode); } else { lastLinkedList.pre.addNode(newNode); } size++; } }
void freqInc(Node node) { DoubleLinkedList linkedList = node.DoubleLinkedList; DoubleLinkedList preLinkedList = linkedList.pre; linkedList.removeNode(node); if (linkedList.head.post == linkedList.tail) { removeDoubleLinkedList(linkedList); } node.freq++; if (preLinkedList.freq != node.freq) { DoubleLinkedList newDoublyLinedList = new DoubleLinkedList(node.freq); addDoubleLinkedList(newDoublyLinedList, preLinkedList); newDoublyLinedList.addNode(node); } else { preLinkedList.addNode(node); } }
void addDoubleLinkedList(DoubleLinkedList newDoublyLinedList, DoubleLinkedList preLinkedList) { newDoublyLinedList.post = preLinkedList.post; newDoublyLinedList.post.pre = newDoublyLinedList; newDoublyLinedList.pre = preLinkedList; preLinkedList.post = newDoublyLinedList; }
void removeDoubleLinkedList(DoubleLinkedList DoubleLinkedList) { DoubleLinkedList.pre.post = DoubleLinkedList.post; DoubleLinkedList.post.pre = DoubleLinkedList.pre; }
}
class Node {
int key; int value; int freq = 1; Node pre; Node post; DoubleLinkedList DoubleLinkedList;
public Node() {}
public Node(int key, int value) { this.key = key; this.value = value; }
}
class DoubleLinkedList {
int freq;
DoubleLinkedList pre; DoubleLinkedList post;
Node head; Node tail;
public DoubleLinkedList() { head = new Node(); tail = new Node(); head.post = tail; tail.pre = head; }
public DoubleLinkedList(int freq) { head = new Node(); tail = new Node(); head.post = tail; tail.pre = head; this.freq = freq; }
void removeNode(Node node) { node.pre.post = node.post; node.post.pre = node.pre; }
void addNode(Node node) { node.post = head.post; head.post.pre = node; head.post = node; node.pre = head; node.DoubleLinkedList = this; } }
|
weiwei
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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
| import java.util.HashMap; import java.util.Map;
public class LFUCache {
private Map<Integer, ListNode> map;
private Map<Integer, DoubleLinkedList> frequentMap;
private Integer capacity;
private Integer minFrequent = 1;
public LFUCache(int capacity) { map = new HashMap<>(capacity); frequentMap = new HashMap<>(); this.capacity = capacity; }
public int get(int key) { if (capacity == 0) { return -1; }
if (map.containsKey(key)) { ListNode listNode = removeListNode(key);
int frequent = listNode.frequent; addListNode2Head(frequent, listNode); return listNode.value; } else { return -1; } }
public void put(int key, int value) { if (capacity == 0) { return; }
if (map.containsKey(key)) { ListNode listNode = removeListNode(key);
listNode.value = value; int frequent = listNode.frequent; addListNode2Head(frequent, listNode); return; }
if (map.size() == capacity) { DoubleLinkedList doubleLinkedList = frequentMap.get(minFrequent); ListNode removeNode = doubleLinkedList.removeTail();
map.remove(removeNode.key); }
ListNode newListNode = new ListNode(key, value); addListNode2Head(1, newListNode); map.put(key, newListNode);
this.minFrequent = 1; }
private class ListNode { private int key; private int value; private int frequent = 1; private ListNode pre; private ListNode next;
public ListNode() { }
public ListNode(int key, int value) { this.key = key; this.value = value; } }
private class DoubleLinkedList {
private ListNode dummyHead;
private ListNode dummyTail;
private int count;
public DoubleLinkedList() { this.dummyHead = new ListNode(-1, -1); this.dummyTail = new ListNode(-2, -2);
dummyHead.next = dummyTail; dummyTail.pre = dummyHead; count = 0; }
public void addNode2Head(ListNode addNode) { ListNode oldHead = dummyHead.next; dummyHead.next = addNode; oldHead.pre = addNode; addNode.pre = dummyHead; addNode.next = oldHead; count++; }
public ListNode removeTail() { ListNode oldTail = dummyTail.pre; ListNode newTail = oldTail.pre;
newTail.next = dummyTail; dummyTail.pre = newTail;
oldTail.pre = null; oldTail.next = null;
count--;
return oldTail; } }
private ListNode removeListNode(int key) { ListNode deleteNode = map.get(key);
ListNode preNode = deleteNode.pre; ListNode nextNode = deleteNode.next; preNode.next = nextNode; nextNode.pre = preNode; deleteNode.pre = null; deleteNode.next = null;
frequentMap.get(deleteNode.frequent).count--;
if (deleteNode.frequent == minFrequent && frequentMap.get(deleteNode.frequent).count == 0) { minFrequent++; }
deleteNode.frequent++; return deleteNode; }
private void addListNode2Head(int frequent, ListNode addNode) { DoubleLinkedList doubleLinkedList;
if (frequentMap.containsKey(frequent)) { doubleLinkedList = frequentMap.get(frequent); } else { doubleLinkedList = new DoubleLinkedList(); }
doubleLinkedList.addNode2Head(addNode); frequentMap.put(frequent, doubleLinkedList); } }
|