@@ -59,27 +59,212 @@ lRUCache.get(4); // 返回 4
59
59
<li>最多调用 <code>3 * 10<sup>4</sup></code> 次 <code>get</code> 和 <code>put</code></li>
60
60
</ul >
61
61
62
-
63
62
## 解法
64
63
65
64
<!-- 这里可写通用的实现逻辑 -->
66
65
66
+ “哈希表 + 双向链表”实现。其中:
67
+
68
+ - 双向链表按照被使用的顺序存储 kv 键值对,靠近头部的 kv 键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
69
+ - 哈希表通过缓存的 key 映射到双向链表中的位置。我们可以在 ` O(1) ` 时间内定位到缓存的 key 所对应的 value 在链表中的位置。
70
+
71
+ 对于 ` get ` 操作,判断 key 是否存在哈希表中:
72
+
73
+ - 若不存在,返回 -1
74
+ - 若存在,则 key 对应的节点 node 是最近使用的节点。将该节点移动到双向链表的头部,最后返回该节点的值即可。
75
+
76
+ 对于 ` put ` 操作,同样先判断 key 是否存在哈希表中:
77
+
78
+ - 若不存在,则创建一个新的 node 节点,放入哈希表中。然后在双向链表的头部添加该节点。接着判断双向链表节点数是否超过 capacity。若超过,则删除双向链表的尾部节点,以及在哈希表中对应的项。
79
+ - 若存在,则更新 node 节点的值,然后该节点移动到双向链表的头部。
80
+
81
+ 双向链表节点(哈希表的 value)的结构如下:
82
+
83
+ ``` java
84
+ class Node {
85
+ int key;
86
+ int value;
87
+ Node prev;
88
+ Node next;
89
+ Node () {
90
+
91
+ }
92
+ Node (int key , int value ) {
93
+ this . key = key;
94
+ this . value = value;
95
+ }
96
+ }
97
+ ```
98
+
99
+ 你可能会问,哈希表的 value 为何还要存放 key?
100
+
101
+ 这是因为,双向链表有一个删除尾节点的操作。我们定位到双向链表的尾节点,在链表中删除之后,还要找到该尾节点在哈希表中的位置,因此需要根据 value 中存放的 key,定位到哈希表的数据项,然后将其删除。
102
+
67
103
<!-- tabs:start -->
68
104
69
105
### ** Python3**
70
106
71
107
<!-- 这里可写当前语言的特殊实现逻辑 -->
72
108
73
109
``` python
110
+ class Node :
111
+ def __init__ (self , key = 0 , value = 0 ):
112
+ self .key = key
113
+ self .value = value
114
+ self .prev = None
115
+ self .next = None
116
+
117
+ class LRUCache :
118
+
119
+ def __init__ (self , capacity : int ):
120
+ self .size = 0
121
+ self .capacity = capacity
122
+ self .cache = {}
123
+ self .head = Node()
124
+ self .tail = Node()
125
+ self .head.next = self .tail
126
+ self .tail.prev = self .head
74
127
128
+ def get (self , key : int ) -> int :
129
+ if key not in self .cache:
130
+ return - 1
131
+ node = self .cache[key]
132
+ self ._move_to_head(node)
133
+ return node.value
134
+
135
+ def put (self , key : int , value : int ) -> None :
136
+ if key not in self .cache:
137
+ new_node = Node(key, value)
138
+ self .cache[key] = new_node
139
+ self ._add_to_head(new_node)
140
+ self .size += 1
141
+ if self .size > self .capacity:
142
+ node = self ._remove_tail()
143
+ self .cache.pop(node.key)
144
+ self .size -= 1
145
+ else :
146
+ node = self .cache[key]
147
+ node.value = value
148
+ self ._move_to_head(node)
149
+
150
+ def _move_to_head (self , node ):
151
+ self ._remove_node(node)
152
+ self ._add_to_head(node)
153
+
154
+ def _remove_node (self , node ):
155
+ node.prev.next = node.next
156
+ node.next.prev = node.prev
157
+
158
+ def _add_to_head (self , node ):
159
+ node.next = self .head.next
160
+ node.next.prev = node
161
+ node.prev = self .head
162
+ self .head.next = node
163
+
164
+ def _remove_tail (self ):
165
+ node = self .tail.prev
166
+ self ._remove_node(node)
167
+ return node
168
+
169
+ # Your LRUCache object will be instantiated and called as such:
170
+ # obj = LRUCache(capacity)
171
+ # param_1 = obj.get(key)
172
+ # obj.put(key,value)
75
173
```
76
174
77
175
### ** Java**
78
176
79
177
<!-- 这里可写当前语言的特殊实现逻辑 -->
80
178
81
179
``` java
180
+ class LRUCache {
181
+ class Node {
182
+ int key;
183
+ int value;
184
+ Node prev;
185
+ Node next;
186
+ Node () {
187
+
188
+ }
189
+ Node (int key , int value ) {
190
+ this . key = key;
191
+ this . value = value;
192
+ }
193
+ }
194
+
195
+ private int size;
196
+ private int capacity;
197
+ private Map<Integer , Node > cache;
198
+ private Node head;
199
+ private Node tail;
200
+
201
+ public LRUCache (int capacity ) {
202
+ this . size = 0 ;
203
+ this . capacity = capacity;
204
+ cache = new HashMap<> ();
205
+ head = new Node ();
206
+ tail = new Node ();
207
+ head. next = tail;
208
+ tail. prev = head;
209
+ }
210
+
211
+ public int get (int key ) {
212
+ Node node = cache. get(key);
213
+ if (node == null ) {
214
+ return - 1 ;
215
+ }
216
+ moveToHead(node);
217
+ return node. value;
218
+ }
219
+
220
+ public void put (int key , int value ) {
221
+ Node node = cache. get(key);
222
+ if (node == null ) {
223
+ Node newNode = new Node (key, value);
224
+ cache. put(key, newNode);
225
+ addToHead(newNode);
226
+ ++ size;
227
+ if (size > capacity) {
228
+ Node tail = removeTail();
229
+ cache. remove(tail. key);
230
+ -- size;
231
+ }
232
+ } else {
233
+ node. value = value;
234
+ moveToHead(node);
235
+ }
236
+ }
237
+
238
+ private void moveToHead (Node node ) {
239
+ removeNode(node);
240
+ addToHead(node);
241
+ }
242
+
243
+ private void removeNode (Node node ) {
244
+ node. prev. next = node. next;
245
+ node. next. prev = node. prev;
246
+ }
247
+
248
+ private void addToHead (Node node ) {
249
+ node. next = head. next;
250
+ head. next = node;
251
+ node. next. prev = node;
252
+ node. prev = head;
253
+ }
254
+
255
+ private Node removeTail () {
256
+ Node node = tail. prev;
257
+ removeNode(node);
258
+ return node;
259
+ }
260
+ }
82
261
262
+ /**
263
+ * Your LRUCache object will be instantiated and called as such:
264
+ * LRUCache obj = new LRUCache(capacity);
265
+ * int param_1 = obj.get(key);
266
+ * obj.put(key,value);
267
+ */
83
268
```
84
269
85
270
### ** ...**
0 commit comments