Skip to content

Commit 4a91fb7

Browse files
committed
feat: update solutions to lc & lcci problems: lru cache
1 parent 59b01ac commit 4a91fb7

File tree

8 files changed

+615
-129
lines changed

8 files changed

+615
-129
lines changed

lcci/16.25.LRU Cache/README.md

+188-2
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,208 @@ cache.get(4); // 返回 4
3131

3232
<!-- 这里可写通用的实现逻辑 -->
3333

34+
“哈希表 + 双向链表”实现。其中:
35+
36+
- 双向链表按照被使用的顺序存储 kv 键值对,靠近头部的 kv 键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
37+
- 哈希表通过缓存的 key 映射到双向链表中的位置。我们可以在 `O(1)` 时间内定位到缓存的 key 所对应的 value 在链表中的位置。
38+
39+
对于 `get` 操作,判断 key 是否存在哈希表中:
40+
41+
- 若不存在,返回 -1
42+
- 若存在,则 key 对应的节点 node 是最近使用的节点。将该节点移动到双向链表的头部,最后返回该节点的值即可。
43+
44+
对于 `put` 操作,同样先判断 key 是否存在哈希表中:
45+
46+
- 若不存在,则创建一个新的 node 节点,放入哈希表中。然后在双向链表的头部添加该节点。接着判断双向链表节点数是否超过 capacity。若超过,则删除双向链表的尾部节点,以及在哈希表中对应的项。
47+
- 若存在,则更新 node 节点的值,然后该节点移动到双向链表的头部。
48+
49+
双向链表节点(哈希表的 value)的结构如下:
50+
51+
```java
52+
class Node {
53+
int key;
54+
int value;
55+
Node prev;
56+
Node next;
57+
Node() {
58+
59+
}
60+
Node(int key, int value) {
61+
this.key = key;
62+
this.value = value;
63+
}
64+
}
65+
```
66+
67+
你可能会问,哈希表的 value 为何还要存放 key?
68+
69+
这是因为,双向链表有一个删除尾节点的操作。我们定位到双向链表的尾节点,在链表中删除之后,还要找到该尾节点在哈希表中的位置,因此需要根据 value 中存放的 key,定位到哈希表的数据项,然后将其删除。
70+
3471
<!-- tabs:start -->
3572

3673
### **Python3**
3774

3875
<!-- 这里可写当前语言的特殊实现逻辑 -->
3976

4077
```python
41-
78+
class Node:
79+
def __init__(self, key=0, value=0):
80+
self.key = key
81+
self.value = value
82+
self.prev = None
83+
self.next = None
84+
85+
class LRUCache:
86+
87+
def __init__(self, capacity: int):
88+
self.cache = {}
89+
self.head = Node()
90+
self.tail = Node()
91+
self.capacity = capacity
92+
self.size = 0
93+
self.head.next = self.tail
94+
self.tail.prev = self.head
95+
96+
def get(self, key: int) -> int:
97+
if key not in self.cache:
98+
return -1
99+
node = self.cache[key]
100+
self.move_to_head(node)
101+
return node.value
102+
103+
def put(self, key: int, value: int) -> None:
104+
if key in self.cache:
105+
node = self.cache[key]
106+
node.value = value
107+
self.move_to_head(node)
108+
else:
109+
node = Node(key, value)
110+
self.cache[key] = node
111+
self.add_to_head(node)
112+
self.size += 1
113+
if self.size > self.capacity:
114+
node = self.remove_tail()
115+
self.cache.pop(node.key)
116+
self.size -= 1
117+
118+
def move_to_head(self, node):
119+
self.remove_node(node)
120+
self.add_to_head(node)
121+
122+
def remove_node(self, node):
123+
node.prev.next = node.next
124+
node.next.prev = node.prev
125+
126+
def add_to_head(self, node):
127+
node.next = self.head.next
128+
self.head.next.prev = node
129+
self.head.next = node
130+
node.prev = self.head
131+
132+
def remove_tail(self):
133+
node = self.tail.prev
134+
self.remove_node(node)
135+
return node
136+
137+
138+
# Your LRUCache object will be instantiated and called as such:
139+
# obj = LRUCache(capacity)
140+
# param_1 = obj.get(key)
141+
# obj.put(key,value)
42142
```
43143

44144
### **Java**
45145

46146
<!-- 这里可写当前语言的特殊实现逻辑 -->
47147

48148
```java
49-
149+
class LRUCache {
150+
class Node {
151+
int key;
152+
int value;
153+
Node prev;
154+
Node next;
155+
Node() {
156+
157+
}
158+
Node(int key, int value) {
159+
this.key = key;
160+
this.value = value;
161+
}
162+
}
163+
164+
private Map<Integer, Node> cache;
165+
private Node head;
166+
private Node tail;
167+
private int capacity;
168+
private int size;
169+
170+
public LRUCache(int capacity) {
171+
cache = new HashMap<>();
172+
this.capacity = capacity;
173+
head = new Node();
174+
tail = new Node();
175+
head.next = tail;
176+
tail.prev = head;
177+
}
178+
179+
public int get(int key) {
180+
if (!cache.containsKey(key)) {
181+
return -1;
182+
}
183+
Node node = cache.get(key);
184+
moveToHead(node);
185+
return node.value;
186+
}
187+
188+
public void put(int key, int value) {
189+
if (cache.containsKey(key)) {
190+
Node node = cache.get(key);
191+
node.value = value;
192+
moveToHead(node);
193+
} else {
194+
Node node = new Node(key, value);
195+
cache.put(key, node);
196+
addToHead(node);
197+
++size;
198+
if (size > capacity) {
199+
node = removeTail();
200+
cache.remove(node.key);
201+
--size;
202+
}
203+
}
204+
}
205+
206+
private void moveToHead(Node node) {
207+
removeNode(node);
208+
addToHead(node);
209+
}
210+
211+
private void removeNode(Node node) {
212+
node.prev.next = node.next;
213+
node.next.prev = node.prev;
214+
}
215+
216+
private void addToHead(Node node) {
217+
node.next = head.next;
218+
head.next.prev = node;
219+
head.next = node;
220+
node.prev = head;
221+
}
222+
223+
private Node removeTail() {
224+
Node node = tail.prev;
225+
removeNode(node);
226+
return node;
227+
}
228+
}
229+
230+
/**
231+
* Your LRUCache object will be instantiated and called as such:
232+
* LRUCache obj = new LRUCache(capacity);
233+
* int param_1 = obj.get(key);
234+
* obj.put(key,value);
235+
*/
50236
```
51237

52238
### **...**

lcci/16.25.LRU Cache/README_EN.md

+151-2
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,162 @@ cache.get(4); // returns 4
4747
### **Python3**
4848

4949
```python
50-
50+
class Node:
51+
def __init__(self, key=0, value=0):
52+
self.key = key
53+
self.value = value
54+
self.prev = None
55+
self.next = None
56+
57+
class LRUCache:
58+
59+
def __init__(self, capacity: int):
60+
self.cache = {}
61+
self.head = Node()
62+
self.tail = Node()
63+
self.capacity = capacity
64+
self.size = 0
65+
self.head.next = self.tail
66+
self.tail.prev = self.head
67+
68+
def get(self, key: int) -> int:
69+
if key not in self.cache:
70+
return -1
71+
node = self.cache[key]
72+
self.move_to_head(node)
73+
return node.value
74+
75+
def put(self, key: int, value: int) -> None:
76+
if key in self.cache:
77+
node = self.cache[key]
78+
node.value = value
79+
self.move_to_head(node)
80+
else:
81+
node = Node(key, value)
82+
self.cache[key] = node
83+
self.add_to_head(node)
84+
self.size += 1
85+
if self.size > self.capacity:
86+
node = self.remove_tail()
87+
self.cache.pop(node.key)
88+
self.size -= 1
89+
90+
def move_to_head(self, node):
91+
self.remove_node(node)
92+
self.add_to_head(node)
93+
94+
def remove_node(self, node):
95+
node.prev.next = node.next
96+
node.next.prev = node.prev
97+
98+
def add_to_head(self, node):
99+
node.next = self.head.next
100+
self.head.next.prev = node
101+
self.head.next = node
102+
node.prev = self.head
103+
104+
def remove_tail(self):
105+
node = self.tail.prev
106+
self.remove_node(node)
107+
return node
108+
109+
110+
# Your LRUCache object will be instantiated and called as such:
111+
# obj = LRUCache(capacity)
112+
# param_1 = obj.get(key)
113+
# obj.put(key,value)
51114
```
52115

53116
### **Java**
54117

55118
```java
56-
119+
class LRUCache {
120+
class Node {
121+
int key;
122+
int value;
123+
Node prev;
124+
Node next;
125+
Node() {
126+
127+
}
128+
Node(int key, int value) {
129+
this.key = key;
130+
this.value = value;
131+
}
132+
}
133+
134+
private Map<Integer, Node> cache;
135+
private Node head;
136+
private Node tail;
137+
private int capacity;
138+
private int size;
139+
140+
public LRUCache(int capacity) {
141+
cache = new HashMap<>();
142+
this.capacity = capacity;
143+
head = new Node();
144+
tail = new Node();
145+
head.next = tail;
146+
tail.prev = head;
147+
}
148+
149+
public int get(int key) {
150+
if (!cache.containsKey(key)) {
151+
return -1;
152+
}
153+
Node node = cache.get(key);
154+
moveToHead(node);
155+
return node.value;
156+
}
157+
158+
public void put(int key, int value) {
159+
if (cache.containsKey(key)) {
160+
Node node = cache.get(key);
161+
node.value = value;
162+
moveToHead(node);
163+
} else {
164+
Node node = new Node(key, value);
165+
cache.put(key, node);
166+
addToHead(node);
167+
++size;
168+
if (size > capacity) {
169+
node = removeTail();
170+
cache.remove(node.key);
171+
--size;
172+
}
173+
}
174+
}
175+
176+
private void moveToHead(Node node) {
177+
removeNode(node);
178+
addToHead(node);
179+
}
180+
181+
private void removeNode(Node node) {
182+
node.prev.next = node.next;
183+
node.next.prev = node.prev;
184+
}
185+
186+
private void addToHead(Node node) {
187+
node.next = head.next;
188+
head.next.prev = node;
189+
head.next = node;
190+
node.prev = head;
191+
}
192+
193+
private Node removeTail() {
194+
Node node = tail.prev;
195+
removeNode(node);
196+
return node;
197+
}
198+
}
199+
200+
/**
201+
* Your LRUCache object will be instantiated and called as such:
202+
* LRUCache obj = new LRUCache(capacity);
203+
* int param_1 = obj.get(key);
204+
* obj.put(key,value);
205+
*/
57206
```
58207

59208
### **...**

0 commit comments

Comments
 (0)