题目:
请设计实现一个最近最少使用(LRU)缓存,要求如下两个操作的时间复杂度都是O(1)。
1.get(key):如果缓存中存在键key,则返回它对应的值:否则返回-1。
2.put(key,value):如果缓存中之前包含键key,则它的值设为value,否则添加键key以及对应的值value。在添加一个键时,如果缓存已经满了,则在添加新键之前删除最近最少使用的键(缓存中最长时间没有被使用过的元素)
分析:
a图是一个容量为4的最近最少使用缓存,依次插入四个键值对后,先后在双向链表中插入4个节点。
b图是使用了get方法get(2),该操作访问键2,它的双向链表中的节点被移动到链表的尾部。
c图是put(1,8),更新键1对应的值,这个操作访问键1,所以链表中的节点移动到链表的尾部。
d图是put(5,5),插入一个新的键5,由于此时缓存已经满了,因此在插入新的键之前要把最近最少使用的键3以及对应的值除掉,再把键5添加到哈希表中,同时在链表尾部插入一个新的节点。
代码:
public class ListNode {
public int val;
public ListNode next;
public int key;
public ListNode prev;
public ListNode(int key,int val) {
this.val = val;
this.key = key;
}
}
import java.util.HashMap;
import java.util.Map;
//定义最近最少使用缓存的数据结构,缓存中包含一个哈希表,哈希表的键就时缓存的键,哈希表的值就是双向链表中
//的节点
public class LRUCache {
private ListNode head;
private ListNode tail;
private Map<Integer,ListNode> map;
int capacity;
public LRUCache(int cap){
map = new HashMap<>();
head = new ListNode(-1,-1);
tail = new ListNode(-1,-1);
head.next = tail;
tail.prev = head;
capacity = cap;
}
}
import java.util.HashMap;
import java.util.Map;
//定义最近最少使用缓存的数据结构,缓存中包含一个哈希表,哈希表的键就时缓存的键,哈希表的值就是双向链表中
//的节点
public class LRUCache {
private ListNode head;
private ListNode tail;
private Map<Integer,ListNode> map;
int capacity;
public LRUCache(int cap){
map = new HashMap<>();
head = new ListNode(-1,-1);
tail = new ListNode(-1,-1);
head.next = tail;
tail.prev = head;
capacity = cap;
}
public int get(int key){
ListNode node = map.get(key);
// 当缓存不存在时,函数get直接返回-1.
if (node == null){
return -1;
}
// 如果缓存包含该键则在返回它对应的值之前先把它在双向链表中对应的值移动到链表的尾部,表示最近访问过该键
moveToTail(node,node.val);
return node.val;
}
private void put(int key,int value){
if (map.containsKey(key)){
moveToTail(map.get(key),value);
}else {
if (map.size() == capacity){
ListNode toBeDeleted = head.next;
deleteNode(toBeDeleted);
map.remove(toBeDeleted.key);
}
ListNode node = new ListNode(key,value);
insertToTail(node);
map.put(key,node);
}
}
// 该方法就是把双向链表中的一个节点移到链表的尾部,移动一个节点实际上包含两个步骤:首先把它从当前位置删除
// 然后添加到链表的尾部,从双向链表中添加和删除节点,都是通过调整链表中的指针实现的。
private void moveToTail(ListNode node,int newValue){
deleteNode(node);
node.val = newValue;
insertToTail(node);
}
private void deleteNode(ListNode node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void insertToTail(ListNode node){
tail.prev.next = node;
node.prev = tail.prev;
node.next = tail;
tail.prev = node;
}
}