双向链表 + 哈希表
应该这样思考,比较合理。
使用双向链表去维护键值对的使用顺序,也就是将刚刚使用的键值对放在链表的头部。
但是,这样的单次操作复杂度是
O
(
n
)
O(n)
O(n)的。
如何优化,使用HashMap即可,
具体来说就是将键与链表的节点一一对应,这样就可以做到O(1)时间复杂度快速检索到关键节点。
class LRUCache {
private int size;
private int capacity;
// 在这里的实现,head ————> tail 是 youngest 到 eldest 的顺序
// 和 LinkedHashMap 的顺序是反过来的。
private Node head, tail;
private HashMap<Integer, Node> map;
private static class Node {
Node prev, next;
int key, val;
public Node(int key,int val) {
this.key = key;
this.val = val;
}
public Node() {
}
}
private void removeNode(Node node) {
Node p1 = node.prev;
Node p2 = node.next;
p1.next = p2;
p2.prev = p1;
}
private void addToHead(Node node) {
Node headNext = head.next;
head.next = node;
node.prev = head;
node.next = headNext;
headNext.prev = node;
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
public LRUCache(int capacity) {
this.capacity = capacity;
head = new Node();
tail = new Node();
head.next = tail;
tail.prev = head;
map = new HashMap<>();
}
public int get(int key) {
if (!map.containsKey(key)) return -1;
Node node = map.get(key);
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
Node node = map.get(key);
if (node == null) {
if (size >= capacity) {
Node last = tail.prev;
removeNode(last);
map.remove(last.key);
} else {
size++;
}
node = new Node(key,value);
addToHead(node);
map.put(key, node);
} else {
node.val = value;
moveToHead(node);
}
}
}
LinkedHashMap
直接使用LinkedHashMap
class LRUCache extends LinkedHashMap<Integer, Integer> {
int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return getOrDefault(key,-1);
}
public void put(int key, int value) {
super.put(key,value);
}
// 将双向链表中最“老”的节点删除
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
// HashMap 会在 put 方法后调用这个方法,所以下面是 >
return size() > capacity;
}
}