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;
}
}

}