核心数据结构
双链表 + 哈希表
双链表节点
由于删除链表最后一个节点时,需要删除对应 map
中的数据,所以数据域需要保存 key
,当然 value
也是必须的。
双链表实现 3 个方法
-
addNode(node)
:在链表头部插入一个节点。 -
removeNode(node)
:删除链表中任意一个节点。 -
removeLast()
:删除链表最后一个节点并返回该节点,目的是得到key
,方便map
也删除对应元素。可以复用removeNode(node)
。
构造方法
- 由于需要删除链表最后一个节点,所以需要一个尾指针
tail
,便于找到最后一个节点。 - 记得初始化
head
和tail
的指针。
get 和 put 逻辑
- 分两种情况:
map
中是否存在该key
。 - 更新
node
的方法:先删除,再添加一遍。
class LRUCache {
class Node{
int key, val;
Node pre, next;
public Node() {}
public Node(int key, int val) { this.key = key; this.val = val; }
}
Node head = new Node(), tail = new Node();
HashMap<Integer, Node> map = new HashMap<>();
int capacity, size;
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.pre = head;
}
public int get(int key) {
if(!map.containsKey(key)) return -1;
Node node = map.get(key);
removeNode(node);
addNode(node);
return node.val;
}
public void put(int key, int value) {
if(!map.containsKey(key)){
Node node = new Node(key, value);
addNode(node);
map.put(key, node);
size++;
if(size > capacity){
Node last = removeLast();
map.remove(last.key);
// size--; // 可以不加, 因为题目没有删除方法, 不影响结果
}
}else{
Node node = map.get(key);
node.val = value;
removeNode(node);
addNode(node);
}
}
void removeNode(Node node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
void addNode(Node node){
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
}
Node removeLast(){
Node node = tail.pre;
removeNode(node); // 复用 removeNode
return node;
}
}