思路
LRU算法,访问/更新/插入都会将数据置于队尾(假设队头淘汰)。
看3种情况的变化:
- 插入:简单置于队尾即可。
- 更新:删除原有节点,新增节点置于队尾。
- 访问:将原节点提至队尾。
除了插入只需要简单接到链表尾部以外,更新和访问都是可能操作链表中间的,所以自然地就需要引入Map来快速找到对应节点。
并且无论是更新的删除原有节点还是访问的将原节点提至队尾,都需要在链表中间删除节点,所以需要双向链表。
首先实现Node类及DoubleList类,然后按照LRU算法思路实现即可。
代码
原始代码
class LRUCache {
private int capacity;
private DoubleList list;
private Map<Integer, Node> map;
public LRUCache(int capacity) {
this.capacity = capacity;
list = new DoubleList();
map = new HashMap<>();
}
public int get(int key) {
if (!map.containsKey(key)) return -1;
Node node = map.get(key);
list.remove(node);
list.addLast(node);
return node.val;
}
public void put(int key, int value) {
Node node = new Node(key, value);
// key不存在
if (!map.containsKey(key)) {
// 链表大小 == 容量
if (list.size() == capacity) {
Node first = list.removeFirst();
int firstkey = first.key;
map.remove(first.key);
int listsize = list.size();
int mapsize = map.size();
int a = key;
}
map.put(key, node);
list.addLast(node);
return;
}
// key存在
Node oldNode = map.get(key);
list.remove(oldNode);
map.put(key, node);
list.addLast(node);
}
}
class DoubleList {
private Node head, tail;
private int size;
public DoubleList() {
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.pre = head;
size = 0;
}
public void addLast(Node node) {
node.pre = tail.pre;
node.next = tail;
tail.pre.next = node;
tail.pre = node;
size++;
}
public void remove(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
size--;
}
public Node removeFirst() {
if (head.next == tail)
return null;
Node first = head.next;
remove(first);
return first;
}
public int size() {
return size;
}
}
class Node {
// key为了方便移出双向链表后,再去删除Map中的键值对
int key;
int val;
Node pre;
Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/