146. LRU 缓存(简单)

1,问题描述

146. 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 ,则应该 逐出 最久未使用的关键字。

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

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 105getput

2,初步思考

​ 模式识别要求时间复杂度O(1)直接就想到了散列表hashMap这种数据结构,要求还要执行删除不经常使用的缓存,那么就是一个双向链表

3,代码处理

第一次的思路想复杂了

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
import java.util.HashMap;
import java.util.Map;

// 要求存取时间复杂度为O(1),要么使用数组,要么使用哈希表,并且哈希表要支持快速删除和插入操作
// hash + 有序队列
class LRUCache {
Map<Integer, DequeNode> map;
DequeNode head;
DequeNode tail;
int capacity;
int counter = 0;
public LRUCache(int capacity) {
map = new HashMap<>();
this.capacity = capacity;

head = new DequeNode(-1000, -1000);
tail = new DequeNode(1000, 1000);

head.next = tail;
tail.before = head;
}

public int get(int key) {
if (map.containsKey(key)) {
DequeNode dequeNode = map.get(key);
// 如果本身就是头节点,就无需操作
if (dequeNode.before != head) {
dequeNode.before.next = dequeNode.next;
dequeNode.next.before = dequeNode.before;// 取出节点,让前后相连

dequeNode.before = head;
dequeNode.next = head.next;// 当前节点的前后连接

head.next.before = dequeNode;
head.next = dequeNode;// 头节点指向当前节点
}
return dequeNode.value;
} else {
return -1;
}
}

public void put(int key, int value) {
if (map.containsKey(key)) {
DequeNode dequeNode = map.get(key);
dequeNode.value = value;
get(key);
return;
}
if (counter == capacity) {
// 容量不够,删除旧节点
DequeNode pre = tail.before;
pre.before.next = pre.next;
pre.next.before = pre.before;

pre.next = null;
pre.before = null;
map.remove(pre.key);
counter--;
}
DequeNode dequeNode = new DequeNode(key, value);
dequeNode.before = head;
dequeNode.next = head.next;
dequeNode.before.next = dequeNode;
dequeNode.next.before = dequeNode;
map.put(key, dequeNode);
counter++;
}
}

class DequeNode {
DequeNode before;
int key;
int value;
DequeNode next;

public DequeNode(int key, int value) {
this.key = key;
this.value = value;
}
}


public class _146LRU缓存 {
public static void main(String[] args) {
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1);
lRUCache.put(2, 2);
System.out.println(lRUCache.get(1));
lRUCache.put(3, 3);
System.out.println(lRUCache.get(2));
lRUCache.put(4, 4);
System.out.println(lRUCache.get(1));
}

}

​ 其实java中自带有相关的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;

public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}

public int get(int key) {
return super.getOrDefault(key, -1);
}

public void put(int key, int value) {
super.put(key, value);
}

@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}

参考链接

Java集合之LinkedHashMap