[LeetCode] 146. LRU Cache

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.
    The functions get and put must each run in O(1) average time complexity.

Example 1:
Input
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4

Constraints:
1 <= capacity <= 3000
0 <= key <= 104
0 <= value <= 105
At most 2 * 105 calls will be made to get and put.

LRU 缓存。

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/lru-cache
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

实现这个缓存器,时间复杂度要求是O(1)。LRU缓存器是在一定的容量范围内存了一些node,按照上一次被访问的时间由新到旧排列,越近被访问的node应该排得越靠前。一个很容易理解的例子就是手机的后台,最近使用的APP最靠前,内存不足的时候,会释放上一次使用时间最远的APP。

思路是双向链表(DLL) + hashmap。因为题意要求了时间复杂度必须是O(1)所以只有 hashmap 和链表才能满足这个时间复杂度。至于为什么是DLL而不是单链表,则是为了node之间的移动方便,也是为了添加删除节点的时候能更加高效。这里解释一下两个函数的实现细节。

get函数,就是在hashmap里面寻找目标。如果有则先删除这个目标,再加进去,再返回,没有则返回-1;同时DLL里面,如果没有这个目标则什么都不做,如果有这个目标则需要把这个node移动到头节点。

put函数,如果node之前存在过,则hashmap没有动作,DLL里面需要把这个node放到头节点;如果node不存在,需要看一下此时此刻node的数量是否超过cache的容量capacity,如果没超过就直接把node放到DLL的头节点,放入hashmap并size++,如果超过了capacity,要先去掉DLL的tail节点,再加入当前节点。

复杂度

时间O(1) - required
空间O(n) - hashmap + DLL

代码

Java实现

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
class LRUCache {
class Node {
int key;
int value;
Node prev;
Node next;
}

private void addNode(Node node) {
// always add the node after the head
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}

private void removeNode(Node node) {
Node prev = node.prev;
Node next = node.next;
prev.next = next;
next.prev = prev;
}

private void moveToHead(Node node) {
removeNode(node);
addNode(node);
}

private Node popTail() {
Node res = tail.prev;
removeNode(res);
return res;
}

private HashMap<Integer, Node> cache = new HashMap<>();
private int size;
private int capacity;
private Node head, tail;

public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new Node();
tail = new Node();
head.next = tail;
tail.prev = head;
}

public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}

public void put(int key, int value) {
Node node = cache.get(key);
if (node == null) {
Node newNode = new Node();
newNode.key = key;
newNode.value = value;
cache.put(key, newNode);
addNode(newNode);
size++;
if (size > capacity) {
Node tail = popTail();
cache.remove(tail.key);
size--;
}
} else {
node.value = value;
moveToHead(node);
}
}
}

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

相关题目

1
2
3
4
146. LRU Cache
460. LFU Cache
622. Design Circular Queue
641. Design Circular Deque

[LeetCode] 146. LRU Cache
https://shurui91.github.io/posts/1072577098.html
Author
Aaron Liu
Posted on
March 1, 2020
Licensed under