Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
Implement the LRUCache
class:
-
LRUCache(int capacity)
Initialize the LRU cache with positive size capacity
. -
int get(int key)
Return the value of the key
if the key exists, otherwise return -1
. -
void put(int key, int value)
Update the value of the key
if the key
exists. Otherwise, add the key-value
pair to the cache. If the number of keys exceeds the capacity
from this operation, evict the least recently used key.
Follow up:
Could you do get
and put
in O(1)
time complexity?
Example 1:
Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]
Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1); // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2); // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1); // return -1 (not found)
lRUCache.get(3); // return 3
lRUCache.get(4); // return 4
Constraints:
-
1 <= capacity <= 3000
-
0 <= key <= 3000
-
0 <= value <= 104
- At most
3 * 104
calls will be made to get
and put
.
LRU缓存器。
实现这个缓存器,时间复杂度要求是O(1)。
LRU缓存器是在一定的容量范围内存了一些node,按照上一次被访问的时间由新到旧排列,越近被访问的node应该排得越靠前。有几个函数需要实现。
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
时间O(1) - required
空间O(n) - hashmap + DLL
思路:
双向链表(DLL) + hashmap。
因为题意要求了时间复杂度必须是O(1)所以只有hashmap才能满足这个时间复杂度。
至于为什么是DLL而不是单链表,则是为了node之间的移动方便,也是为了添加删除节点的时候能更加高效。
class LRUCache {
/**
* map<K,Node>
*/
private HashMap<Integer,Node> keyNodeMap;
/**
* 自己组织的双链表
*/
private NodeDoubleLinkedList nodeList;
/**
* 缓存容量
*/
private Integer capacity;
/***
* 构造函数初始化结构
*/
public LRUCache(Integer capacity) {
if (capacity < 1) {
throw new RuntimeException("capacity should be more than 0");
}
keyNodeMap = new HashMap<>();
nodeList = new NodeDoubleLinkedList();
this.capacity = capacity;
}
/**
* get(k)
*/
public int get(int k) {
//是否在Map中
if (keyNodeMap.containsKey(k)) {
//通过K 定位到Node
Node res = keyNodeMap.get(k);
//刷新nodeList
this.nodeList.moveNodeToTail(res);
return res.v;
}
return -1;
}
/**
* set(k,v)
*/
public void put(int k, int v) {
//如已存在于表中,则刷新节点,并更新node.v
if (keyNodeMap.containsKey(k)) {
Node res = keyNodeMap.get(k);
res.v = v;
this.nodeList.moveNodeToTail(res);
} else {
//不存在则加入
Node node = new Node(k, v);
//放入表中
keyNodeMap.put(k, node);
//加入到链表上
this.nodeList.addNode(node);
//如果容量超限,则移除头节点,交从表中删除
if (keyNodeMap.size() == capacity + 1) {
Node removeNode = this.nodeList.removeHead();
keyNodeMap.remove(removeNode.k);
}
}
}
public class Node {
public int k;
public int v;
private Node pre;
private Node next;
public Node(int k, int v) {
this.k = k;
this.v = v;
}
}
/**
* 先准备双链表的api
*/
public class NodeDoubleLinkedList {
private Node head;
private Node tail;
public NodeDoubleLinkedList() {
this.head = null;
this.tail = null;
}
/**
* 添加节点的操作,添加到结尾
*/
public void addNode(Node node) {
//如等待加的节点为空,直接返回
if (node == null) {
return;
}
//如果head为null,则挂在头节点
if (head == null) {
head = node;
tail = node;
}
//否则挂在结尾的节点
tail.next=node;
node.pre=tail;
node.next=null;
tail=node;
}
/**
* 先保证这个节点存在的情况下考虑此问题
* 如果移动节点到尾部:先断联,再加到结尾节点
*/
public void moveNodeToTail(Node node) {
//如果要移动的是尾节点,则不用操作
if (this.tail == node) {
return;
}
//先断联
//如果要移动的是头节点,更改头节点为老头的下一个节点
if(head==node){
head=head.next;
head.pre=null;
}else{//如果不是头尾节点,则是中间节点,先断联
node.pre.next=node.next;
node.next.pre=node.pre;
}
//再移到尾节点
tail.next=node;
node.pre=tail;
node.next=null;
tail=node;
}
/**
* 移除节点
*/
public Node removeHead() {
//你要操作头节点,则必须先检查head的状态
//如果head null
if (head == null) {
return null;
}
//记录一下老头
Node res = head;
//如果头节点是head==tail
if (head == tail) {
head = null;
tail = null;
} else {//链表节点个数>1
head = res.next;
head.pre = null;
res.next = null;//help gc
}
return res;
}
}
}