题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
样例
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
题解
- LRU是OS中页面置换算法的一种,称为最近最久未使用算法。缺页时,计算内存中每个逻辑页面的上一次访问时间,移除上一次使用到当前时间最长的页面,即选择最长时间没有被访问的页面进行置换。
- 在这道题中,我们利用哈希表和双端链表处理,哈希表存储每次添加的元素,并且将新操作的元素链接到双端链表的尾部。
- 在双端链表中,头结点表示优先级最低,尾结点表示优先级最高。
- 每次添加元素先判断map中是否存在,若存在则用新值覆盖旧值,并将链表中对应的节点移至尾部(即提升优先级);若不存在则将元素put到map中,并添加至链表尾部,然后判断是否超出容量,若超出则移除链表头部元素(即优先级最低的元素),并将该元素从map中移除。
import java.util.HashMap;
class Node{
public int key;
public int val;
public Node pre;
public Node next;
public Node(int key,int val) {
this.val=val;
this.key=key;
}
}
class NodeDoubleLinkedList{
private Node head;
private Node tail;
public NodeDoubleLinkedList() {
this.head=null;
this.tail=null;
}
public void addNode(Node newNode) {
if(newNode==null)
return;
if(this.head==null) {
this.head=newNode;
this.tail=newNode;
}else {
this.tail.next=newNode;
newNode.pre=this.tail;
this.tail=newNode;
}
}
public void moveNodeToTail(Node node) {
if(this.tail==node)
return;
if(this.head==node) {
this.head=node.next;
this.head.pre=null;
}else {
node.pre.next=node.next;
node.next.pre=node.pre;
}
node.pre=this.tail;
node.next=null;
this.tail.next=node;
this.tail=node;
}
public Node removeHead() {
if(this.head==null)
return null;
Node res=this.head;
if(this.head==this.tail) {
this.head=null;
this.tail=null;
}else {
this.head=res.next;
res.next=null;
this.head.pre=null;
}
return res;
}
}
public class LRUCache {
private int capacity;
private HashMap<Integer,Node> nodeMap;
private NodeDoubleLinkedList list;
public LRUCache(int capacity) {
if(capacity<1)
throw new RuntimeException("error");
this.nodeMap=new HashMap<>();
this.list=new NodeDoubleLinkedList();
this.capacity=capacity;
}
public int get(int key) {
if(nodeMap.containsKey(key)) {
Node res=nodeMap.get(key);
this.list.moveNodeToTail(res);
return res.val;
}
return -1;
}
public void put(int key, int value) {
if(nodeMap.containsKey(key)) {
Node res=nodeMap.get(key);
res.val=value;
list.moveNodeToTail(res);
}else {
Node newNode=new Node(key,value);
nodeMap.put(key, newNode);
list.addNode(newNode);
if(this.nodeMap.size()==capacity+1) {
this.removeMostUnused();
}
}
}
private void removeMostUnused() {
Node res=list.removeHead();
nodeMap.remove(res.key);
}
public static void main(String[] args) {
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
System.out.println(cache.get(1)); // 返回 1
cache.put(3, 3); // 该操作会使得2移除
System.out.println(cache.get(2)); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得1移除
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
}
}