【主要考察点】:哈希表、链表
解题方法
1. 哈希表+双向链表
- 哈希表用来映射元素在双向链表中的位置,另外提供get、put功能。
- 双向链表通过不断的调整元素在链表中的位置来实现LRU功能,越最近访问的越靠近链表头部,如果需要淘汰,则直接移除链表尾部元素即可。
import java.util.HashMap;
import java.util.Map;
public class LRUCache {
// 记录当前集合大小
int size = 0;
// 阈值
int capacity;
// 虚拟一个头节点(方便边界处理)
LinkNode head;
// 虚拟一个尾节点(方便边界处理)
LinkNode tail;
Map<Integer, LinkNode> map = new HashMap<>();
public LRUCache(int capacity) {
this.capacity = capacity;
// 初始化头尾节点
this.head = new LinkNode();
this.tail = new LinkNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
LinkNode node = map.get(key);
if (node == null) {
return -1;
}
// 访问过之后,要移动到链表头部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
LinkNode node = map.get(key);
if (node == null) {
// 构建一个新节点
LinkNode newNode = new LinkNode(key, value);
// 添加到链表头节点
addToHead(newNode);
size++;
map.put(key, newNode);
// 如果此时超过阈值,则移除尾节点
if (size > capacity) {
LinkNode tail = removeTail();
size--;
map.remove(tail.key);
}
} else {
// 更新value
node.value = value;
// 移动到链表头部
moveToHead(node);
}
}
private LinkNode removeTail() {
LinkNode node = tail.prev;
removeNode(node);
return node;
}
private void moveToHead(LinkNode node) {
// 先把节点从链表中删除
removeNode(node);
// 然后再添加到链表头部
addToHead(node);
}
private void removeNode(LinkNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void addToHead(LinkNode node) {
node.next = head.next;
head.next.prev = node;
head.next = node;
node.prev = head;
}
}
/**
* 双向链表
*/
class LinkNode {
int key;
int value;
// 前一个节点
LinkNode prev;
// 后一个节点
LinkNode next;
public LinkNode() {
}
public LinkNode(int key, int value) {
this.key = key;
this.value = value;
}
}