题目描述
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
思考点 实现逻辑
看到此题目
因为get 能直接取到 首先想到的就是map 没有问题。
但是 想要put 直接放到合适的位置应该怎么办?
1.如果map 加数组来实现 map 的value 放数组的索引位置。这样 get 也能直接取到。但是调整整个map的顺序的话 即使整体向右平移 也需要n次的操作。想不通 换方法
2.使用map 加链表的方式。map 的value存上node 节点 ,node节点里有4个属性 分别是上一个节点 下一个节点,当前的key 当前的value.这样是能通的。
每次取值的话 直接就取到node 的value就可以了,取过值以后。需要重新排序。只需要将当前的节点放到头节点上。其它的节点依次链接。如何能常量级的放到头结点呢? 我们需要记录一下头结点是什么。
放值的时候。如果超出容量限制,需要移除一个队尾。我们记录容量限制 和当前容量。方便对比。为了快速移除队尾。我们还需要记录一下尾节点是什么。
实现代码
public class LRUCache {
private Map<Integer,Node> data = new HashMap<>();
private Node first;
private Node last;
int capacity;
int size = 0;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Node current = data.get(key);
if(current == null) return -1;
this.update(current);
return current.val;
}
public void put(int key, int value) {
Node current = this.data.get(key);
if(current == null){
Node node = new Node(value, key);
this.data.put(key, node);
Node oldFirst = this.first;
if(oldFirst != null) {
oldFirst.left = node;
node.right = oldFirst;
}
this.first = node;
if(this.last == null) this.last = node;
this.size += 1;
}else {
current.val = value;
this.update(current);
}
this.removeLast();
}
/**
* 调整次序
*/
public void update(Node current){
if(current.key == this.first.key){
//当前就是队头 不调整
} else if (current.key == this.last.key) {
// 当前是队尾 移除,将倒数第二放队尾
Node newLast = current.left;
this.last = newLast;
Node oldFirst = this.first;
oldFirst.left = current;
current.right = oldFirst;
current.left = null;
this.first = current;
}else {
// 当前在中间 挑出来,放队首 重新链接
Node oldLeft = current.left;
Node oldRight = current.right;
oldLeft.right = oldRight;
oldRight.left = oldLeft;
Node oldFirst = this.first;
oldFirst.left = current;
current.right = oldFirst;
current.left = null;
this.first = current;
}
}
/**
* 移除最后一个
*/
public void removeLast(){
if(this.size > this.capacity){
Node oldLast = this.last;
Node newLast = oldLast.left;
newLast.right = null;
this.last = newLast;
this.size -= 1;
this.data.remove(oldLast.key);
}
}
}
class Node{
public int val;
public int key;
public Node left;
public Node right;
public Node(int val, int key) {
this.val = val;
this.key = key;
}
}