思路分析
LRU(最近最少使用)的设计策略重点在于维护固定格式的缓存,这里用双向链表来维护这些缓存数据,更准确的来说是利用哈希表和双向链表来完成这一设计,哈希表的作用是在O(1)的时间内完成查找,双端链表则是用来保持按使用频率排序的操作,这个题目我们需要理解的是,如果进行了插入和取出,那么就要对缓存的数据进行排序。
因为数据既有key又有value,那么肯定需要使用到HashMap,并且我们如果插入或者取出数据后都要排序,并且set和get的时间复杂度都要是O(1),那么必须使用双向链表来维持缓存数据,因为如果是单向的链表删除数据的时候无法做到O(1)时间复杂度。
class LRUCache {
/*
定义一个Node
*/
class Node{
int key;
int value;
Node next;
Node pre;
public Node(int key, int value){
this.key = key;
this.value = value;
}
}
//需要定义一个map
private HashMap<Integer,Node> map;
//定义缓存容量
private int capacity;
//定义头结点
private Node head;
//定义尾结点
private Node tail;
public LRUCache(int capacity) {
//对这个cache进行初始化
map = new HashMap<>();
this.capacity = capacity;
head = null;
tail = null;
}
public int get(int key) {
Node node = map.get(key);
if(node==null){
return -1;
}
//重新排序
if(node!=tail){
if(node==head){
head = head.next;
}else{
node.pre.next = node.next;
node.next.pre = node.pre;
}
tail.next = node;
node.pre = tail;
node.next = null;
tail = node;
}
return node.value;
}
public void put(int key, int value) {
Node node = map.get(key);
if(node!=null){
node.value = value;
//重新排序
if(node!=tail){
if(node==head){
head = head.next;
}else{
node.pre.next = node.next;
node.next.pre = node.pre;
}
tail.next = node;
node.pre = tail;
node.next = null;
tail = node;
}
}else{
Node newNode = new Node(key, value);
if(capacity==0){
Node temp = head;
head = head.next;
if(temp==tail){
tail = null;
}
map.remove(temp.key);
capacity++;
}
if(head==null&&tail==null){
head = newNode;
}else{
tail.next = newNode;
newNode.pre = tail;
newNode.next = null;
}
tail = newNode;
map.put(key,newNode);
capacity--;
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/