Skip to content

Commit e2ac6d9

Browse files
committed
二刷146
1 parent 81c6d77 commit e2ac6d9

File tree

4 files changed

+156
-30
lines changed

4 files changed

+156
-30
lines changed

docs/0146-lru-cache.adoc

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,74 @@
11
[#0146-lru-cache]
2-
= 146. LRU Cache
2+
= 146. LRU 缓存
33

4-
{leetcode}/problems/lru-cache/[LeetCode - LRU Cache^]
4+
https://leetcode.cn/problems/lru-cache/[LeetCode - 146. LRU 缓存 ^]
55

6-
使用双向链表实现时,使用两个"空节点" `head``tail`,可以有效减少空值判断,真是精巧。想起来上大学时,老师讲课提到这么一句。没想到竟然是如此实现
6+
请你设计并实现一个满足 https://baike.baidu.com/item/LRU[LRU (最近最少使用) 缓存] 约束的数据结构
77

8-
`LinkedHashMap` 的实现也有不少的秘密可以探索。
8+
实现 `LRUCache` 类:
99

10-
Design and implement a data structure for https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU[Least Recently Used (LRU) cache^]. It should support the following operations: `get` and `put`.
10+
* `LRUCache(int capacity)`*正整数* 作为容量 `capacity` 初始化 LRU 缓存
11+
* `int get(int key)` 如果关键字 `key` 存在于缓存中,则返回关键字的值,否则返回 `-1`
12+
* `void put(int key, int value)` 如果关键字 `key` 已经存在,则变更其数据值 `value`;如果不存在,则向缓存中插入该组 `key-value`。如果插入操作导致关键字数量超过 `capacity` ,则应该 *逐出* 最久未使用的关键字。
1113
12-
`get(key)` - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
14+
函数 `get``put` 必须以 `O(1)` 的平均时间复杂度运行。
1315

16+
*示例:*
1417

15-
`put(key, value)` - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
18+
....
19+
输入
20+
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
21+
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
22+
输出
23+
[null, null, null, 1, null, -1, null, -1, 3, 4]
1624
17-
The cache is initialized with a *positive* capacity.
25+
解释
26+
LRUCache lRUCache = new LRUCache(2);
27+
lRUCache.put(1, 1); // 缓存是 {1=1}
28+
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
29+
lRUCache.get(1); // 返回 1
30+
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
31+
lRUCache.get(2); // 返回 -1 (未找到)
32+
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
33+
lRUCache.get(1); // 返回 -1 (未找到)
34+
lRUCache.get(3); // 返回 3
35+
lRUCache.get(4); // 返回 4
36+
....
1837

19-
*Follow up:*
38+
*提示:*
2039

40+
* `+1 <= capacity <= 3000+`
41+
* `+0 <= key <= 10000+`
42+
* `0 \<= value \<= 10^5^`
43+
* 最多调用 `2 * 10^5^``get``put`
2144
22-
Could you do both operations in *O(1)* time complexity?
2345
24-
*Example:*
46+
== 思路分析
2547

26-
[subs="verbatim,quotes,macros"]
27-
----
28-
LRUCache cache = new LRUCache( 2 /* capacity */ );
29-
30-
cache.put(1, 1);
31-
cache.put(2, 2);
32-
cache.get(1); // returns 1
33-
cache.put(3, 3); // evicts key 2
34-
cache.get(2); // returns -1 (not found)
35-
cache.put(4, 4); // evicts key 1
36-
cache.get(1); // returns -1 (not found)
37-
cache.get(3); // returns 3
38-
cache.get(4); // returns 4
39-
----
48+
使用双向链表实现时,使用两个"空节点" `head` 和 `tail`,可以有效减少空值判断,真是精巧。想起来上大学时,老师讲课提到这么一句。没想到竟然是如此实现。
4049

50+
`LinkedHashMap` 的实现也有不少的秘密可以探索。
4151

4252

43-
44-
4553
[[src-0146]]
54+
[tabs]
55+
====
56+
一刷::
57+
+
58+
--
4659
[{java_src_attr}]
4760
----
4861
include::{sourcedir}/_0146_LRUCache.java[tag=answer]
4962
----
63+
--
64+
65+
二刷::
66+
+
67+
--
68+
[{java_src_attr}]
69+
----
70+
include::{sourcedir}/_0146_LRUCache_2.java[tag=answer]
71+
----
72+
--
73+
====
5074

logbook/202503.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ endif::[]
105105
|{doc_base_url}/0003-longest-substring-without-repeating-characters.adoc[题解]
106106
|✅ 滑动窗口
107107

108+
|{counter:codes2503}
109+
|{leetcode_base_url}/lru-cache/[146. LRU 缓存^]
110+
|{doc_base_url}/0146-lru-cache.adoc[题解]
111+
|✅ 链表前后指针操作
112+
108113
|===
109114

110115
截止目前,本轮练习一共完成 {codes2503} 道题。

src/main/java/com/diguage/algo/leetcode/_0146_LRUCache.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public class _0146_LRUCache {
4747
* Memory Usage: 50.8 MB, less than 96.93% of Java online submissions for LRU Cache.
4848
*
4949
* Copy from: https://leetcode-cn.com/problems/lru-cache/solution/lru-huan-cun-ji-zhi-by-leetcode/[LRU 缓存机制 - LRU缓存机制 - 力扣(LeetCode)]
50+
*
51+
* @author D瓜哥 · https://www.diguage.com
52+
* @since 2020-01-26 10:49
5053
*/
5154
class LRUCache {
5255
private Map<Integer, DLinkedNode> data;
@@ -155,6 +158,8 @@ public void put(int key, int value) {
155158
}
156159
}
157160

161+
// end::answer[]
162+
158163
private void test() {
159164
LRUCache solution = new LRUCache(2);
160165
solution.put(1, 1);
@@ -173,9 +178,6 @@ private void test() {
173178
System.out.println((r5 == 4) + " : " + r5);
174179
}
175180

176-
// end::answer[]
177-
178-
179181
public static void main(String[] args) {
180182
_0146_LRUCache solution = new _0146_LRUCache();
181183
solution.test();
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.diguage.algo.leetcode;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class _0146_LRUCache_2 {
7+
// tag::answer[]
8+
9+
/**
10+
* 最近最少使用缓存
11+
*
12+
* @author D瓜哥 · https://www.diguage.com
13+
* @since 2025-04-01 23:28:56
14+
*/
15+
class LRUCache {
16+
17+
private Map<Integer, Node<Integer>> data;
18+
private Node<Integer> head;
19+
private Node<Integer> tail;
20+
private int capacity;
21+
22+
public LRUCache(int capacity) {
23+
head = new Node<>();
24+
tail = new Node<>();
25+
head.next = tail;
26+
tail.prev = head;
27+
data = new HashMap<>(capacity);
28+
this.capacity = capacity;
29+
}
30+
31+
public int get(int key) {
32+
Node<Integer> node = data.getOrDefault(key, null);
33+
if (node == null) {
34+
return -1;
35+
}
36+
siftUp(node);
37+
return node.item;
38+
}
39+
40+
private void siftUp(Node<Integer> node) {
41+
// 将当前访问节点放在链表最前面
42+
node.prev.next = node.next;
43+
node.next.prev = node.prev;
44+
45+
node.next = head.next;
46+
node.prev = head;
47+
48+
head.next.prev = node;
49+
head.next = node;
50+
51+
}
52+
53+
public void put(int key, int value) {
54+
Node<Integer> node;
55+
if (data.containsKey(key)) {
56+
node = data.get(key);
57+
node.item = value;
58+
siftUp(node);
59+
} else {
60+
node = new Node<>(head, key, value, head.next);
61+
data.put(key, node);
62+
head.next.prev = node;
63+
head.next = node;
64+
}
65+
if (data.size() > capacity) {
66+
Node<Integer> removing = tail.prev;
67+
removing.prev.next = tail;
68+
tail.prev = removing.prev;
69+
70+
removing.prev = null;
71+
removing.next = null;
72+
73+
data.remove(removing.key);
74+
}
75+
}
76+
77+
static class Node<E> {
78+
E key;
79+
E item;
80+
Node<E> next;
81+
Node<E> prev;
82+
83+
Node() {
84+
}
85+
86+
Node(Node<E> prev, E key, E element, Node<E> next) {
87+
this.key = key;
88+
this.item = element;
89+
this.next = next;
90+
this.prev = prev;
91+
}
92+
}
93+
}
94+
// end::answer[]
95+
}

0 commit comments

Comments
 (0)