LettoCode460-LFU 缓存

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
  • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1
  • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity
    时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 *
    最久未使用* 的键。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 getput 操作,使用计数器的值将会递增。

函数 getput 必须以 O(1) 的平均时间复杂度运行

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

class LFUCache {

int minfreq;
int capacity;
Map<Integer, Node> keyTable;
Map<Integer,DoublyLinkedList> freqTable;
public LFUCache(int capacity) {
this.minfreq =0 ;
this.capacity = capacity;
keyTable = new HashMap<Integer,Node>();
freqTable = new HashMap<Integer,DoublyLinkedList>();


}

public int get(int key) {
if (capacity == 0) {
return -1;
}
if (!keyTable.containsKey(key)) {
return -1;
}
Node node = keyTable.get(key);
int val = node.val, freq = node.freq;
freqTable.get(freq).remove(node);
// 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
if (freqTable.get(freq).size == 0) {
freqTable.remove(freq);
if (minfreq == freq) {
minfreq += 1;
}
}
// 插入到 freq + 1 中
DoublyLinkedList list = freqTable.getOrDefault(freq + 1, new DoublyLinkedList());
list.addFirst(new Node(key, val, freq + 1));
freqTable.put(freq + 1, list);
keyTable.put(key, freqTable.get(freq + 1).getHead());
return val;
}

public void put(int key, int value) {
if (capacity == 0) {
return;
}
if (!keyTable.containsKey(key)) {
// 缓存已满,需要进行删除操作
if (keyTable.size() == capacity) {
// 通过 minFreq 拿到 freqTable[minFreq] 链表的末尾节点
Node node = freqTable.get(minfreq).getTail();
keyTable.remove(node.key);
freqTable.get(minfreq).remove(node);
if (freqTable.get(minfreq).size == 0) {
freqTable.remove(minfreq);
}
}
DoublyLinkedList list = freqTable.getOrDefault(1, new DoublyLinkedList());
list.addFirst(new Node(key, value, 1));
freqTable.put(1, list);
keyTable.put(key, freqTable.get(1).getHead());
minfreq = 1;
} else {
// 与 get 操作基本一致,除了需要更新缓存的值
Node node = keyTable.get(key);
int freq = node.freq;
freqTable.get(freq).remove(node);
if (freqTable.get(freq).size == 0) {
freqTable.remove(freq);
if (minfreq == freq) {
minfreq += 1;
}
}
DoublyLinkedList list = freqTable.getOrDefault(freq + 1, new DoublyLinkedList());
list.addFirst(new Node(key, value, freq + 1));
freqTable.put(freq + 1, list);
keyTable.put(key, freqTable.get(freq + 1).getHead());
}
}


// 初始化Node DoublyLinkedList
class Node {
int key, val, freq;
Node prev, next;

Node() {
this(-1, -1, 0);
}

Node(int key, int val, int freq) {
this.key = key;
this.val = val;
this.freq = freq;
}
}



class DoublyLinkedList {
Node dummyHead, dummyTail;
int size;

DoublyLinkedList() {
dummyHead = new Node();
dummyTail = new Node();
dummyHead.next = dummyTail;
dummyTail.prev = dummyHead;
size = 0;
}

public void addFirst(Node node) {
Node prevHead = dummyHead.next;
node.prev = dummyHead;
dummyHead.next = node;
node.next = prevHead;
prevHead.prev = node;
size++;
}

public void remove(Node node) {
Node prev = node.prev, next = node.next;
prev.next = next;
next.prev = prev;
size--;
}

public Node getHead() {
return dummyHead.next;
}

public Node getTail() {
return dummyTail.prev;
}

}


}

/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache obj = new LFUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

__END__