可以用链表 + 哈希表的方法来做。链表的作用是存储key和value,哈希表的作用是存储key和key对应的node的前一个node,这是考虑到删除的情况下,需要知道要删除的node的前一个node。如果用双向链表的话,则直接存储对应的node即可。
具体的方法是,如果要get某个值时,先去哈希表里查这个值有没有,没有直接返回-1,否则得到这个key对应的节点的前一个节点,然后利用这个节点删除掉那个对应的节点,接着在链表尾新建一个对应节点的拷贝,这样就达到了“最近get过的节点都在末尾,最近没get过的节点都在链表头”的效果,一旦到了capacity,就直接把链表头部节点删掉即可。
如果要put某个值时,做法和get类似,也是要先查一下有没有那个key,没有的话直接在表尾加,否则也是把中间那个node删掉再在末尾新建一个拷贝。
import java.util.HashMap;
import java.util.LinkedHashMap;
public class LRUCache {
// 创立一个单链表节点的class,里面存key和value
class ListNode {
int key, val;
ListNode next;
ListNode(int key, int val) {
this.key = key;
this.val = val;
}
}
// 需要两个节点来标记链表头和尾,方便删除或添加
private ListNode dummy, tail;
// key是cache的key,value是key对应的node的前一个节点(方便对此节点做删除操作)
private HashMap<Integer, ListNode> map;
private int capacity;
public LRUCache(int capacity) {
dummy = new ListNode(0, 0);
tail = dummy;
map = new HashMap<>();
this.capacity = capacity;
}
public int get(int key) {
// 先去哈希表里查有没有这个key,没有就直接返回-1
ListNode prev = map.get(key);
if (prev == null) {
return -1;
}
// 接着先在尾处添上一份拷贝
tail.next = new ListNode(key, prev.next.val);
// 把这份拷贝覆盖掉哈希表里原有的那个
map.put(key, tail);
// 更新tail
tail = tail.next;
// 删除掉旧的那个
prev.next = prev.next.next;
// 更新旧的那个的next对应的哈希表里的记录
map.put(prev.next.key, prev);
return tail.val;
}
public void put(int key, int value) {
// 得到旧的node之前的那个node
ListNode old = map.get(key);
// 先在尾部新建一份拷贝
tail.next = new ListNode(key, value);
map.put(key, tail);
tail = tail.next;
// 如果之前已经存在,就将其删掉。注意每次修改链表的时候,都要去更新一下哈希表
if (old != null) {
old.next = old.next.next;
map.put(old.next.key, old);
}
if (map.size() > capacity) {
map.remove(dummy.next.key);
dummy.next = dummy.next.next;
map.put(dummy.next.key, dummy);
}
}
}