【经典算法题】LRU缓存机制
Leetcode 0146 LRU缓存机制
分析
-
本题的考点:哈希表、双向链表。
-
Java
中可以直接使用LinkedHashMap
就可以。 -
初始化一个双向链表,双向链表中的节点中存储键、值、左指针、右指针;新建虚拟头结点
L
和虚拟尾结点R
,让其相互指向。 -
对于
get
函数:如果不存在直接返回-1;如果存在通过哈希表以及键找到这个节点p
,并将其移到头结点(先让p
从链表中删除,然后再将p
插入到虚拟头结点后面)。 -
对于
put
函数:如果存在,直接更新节点对应的值,然后将该节点移到头结点;如果不存在则判断缓存是否已满,如果已满删除尾结点,然后将新数据插入到头结点中。 -
可以发现上述存在两个操作:删除某个节点
p
,向虚拟头结点后插入一个节点p
,这两种操作的示意图如下:
代码
- C++
class LRUCache {
public:
struct Node {
int key, val;
Node *left, *right;
Node (int _key, int _val): key(_key), val(_val), left(NULL), right(NULL) {}
} *L, *R; // L: 虚拟头结点; R: 虚拟尾结点
unordered_map<int, Node*> hash; // (键, 该键对应的节点)
int n;
LRUCache(int capacity) {
n = capacity;
L = new Node(-1, -1), R = new Node(-1, -1);
L->right = R, R->left = L;
}
int get(int key) {
if (!hash.count(key)) return -1;
auto p = hash[key];
remove(p); // 从双向链表中删除p
insert(p); // 在虚拟头结点L后插入p
return p->val;
}
void put(int key, int value) {
if (hash.count(key)) {
auto p = hash[key];
p->val = value;
remove(p);
insert(p);
} else {
if (hash.size() == n) {
auto p = R->left;
remove(p);
hash.erase(p->key);
delete p;
}
auto p = new Node(key, value);
insert(p);
hash[key] = p;
}
}
// 辅助函数
void remove(Node *p) { // 从双向链表中删除p
p->left->right = p->right;
p->right->left = p->left;
}
void insert(Node *p) { // 在虚拟头结点L后插入p
p->right = L->right;
p->left = L;
L->right->left = p;
L->right = p;
}
};
- Java
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;
}
}
class LRUCache {
static class Node {
int key, val;
Node left, right;
public Node(int _key, int _val) {
key = _key; val = _val; left = null; right = null;
}
}
Node L = new Node(-1, -1), R = new Node(-1, -1);
HashMap<Integer, Node> hash = new HashMap<>();
int n;
public LRUCache(int capacity) {
n = capacity;
L.right = R; R.left = L;
}
public int get(int key) {
if (!hash.containsKey(key)) return -1;
else {
Node p = hash.get(key);
remove(p);
insert(p);
return p.val;
}
}
public void put(int key, int value) {
if (hash.containsKey(key)) {
Node p = hash.get(key);
p.val = value;
remove(p);
insert(p);
} else {
if (hash.size() == n) {
Node p = R.left;
remove(p);
hash.remove(p.key);
p = null; // help GC
}
Node p = new Node(key, value);
hash.put(key, p);
insert(p);
}
}
// 辅助函数
private void remove(Node p) {
p.left.right = p.right;
p.right.left = p.left;
}
private void insert(Node p) {
p.right = L.right;
p.left = L;
L.right.left = p;
L.right = p;
}
}
时空复杂度分析
-
时间复杂度: O ( 1 ) O(1) O(1)。
-
空间复杂度:和操作次数有关。