1. 题目

https://leetcode.cn/problems/lru-cache/
2. 解法
解题思路是
使用一个双向链表和一个哈希表来实现一个LRU缓存机制。
双向链表用来存储最常使用的键值对,最近使用的元素放在链表的表头,链表中最后一个元素是使用频率最低的元素。
哈希表用来记录对应的<key,<key, value>>,用于查找现在的缓存中是否有key及其value,以及快速定位链表中的节点。
当需要获取数据时,先在哈希表中查找是否有对应的key,如果有,则返回对应的value,并将该节点移动到链表的表头。如果没有,则返回-1。
当需要写入数据时,先在哈希表中查找是否有对应的key,如果有,则更新对应的value,并将该节点移动到链表的表头。如果没有,则在链表的表头插入新的节点,并在哈希表中添加新的映射。如果此时缓存已满,则删除链表的最后一个节点,并在哈希表中删除对应的映射。
这道题的解题思路可以分为以下几个步骤:
- 创建一个双向链表和一个哈希表。
- 定义双向链表的节点类,包含key,value,pre和next四个属性。
- 定义双向链表的数据结构,包含head,tail,size三个属性,以及addFirst,remove,removeLast和size四个方法。
- 定义LRU缓存的数据结构,包含map,cache和cap三个属性,以及get和put两个方法。
- 在get方法中,先在map中查找key是否存在,如果存在,则返回value,并将节点移动到链表头部;如果不存在,则返回-1。
- 在put方法中,先在map中查找key是否存在,如果存在,则更新value,并将节点移动到链表头部;如果不存在,则在链表头部添加新节点,并在map中添加映射;如果缓存已满,则删除链表尾部的节点,并在map中删除映射。
class LRUCache {
//定义双向链表的节点类
class Node{
int key;
int value;
Node pre;
Node next;
public Node(int key,int value){
this.key=key;
this.value=value;
}
}
//定义双向链表的数据结构
class DoubleList{
//头尾虚节点
Node head,tail;
//链表元素数
int size;
public DoubleList(){
//初始化双向链表的数据
head=new Node(0,0);
tail=new Node(0,0);
head.next=tail;
tail.pre=head;
size=0;
}
//在链表头部添加节点x,时间O(1)
public void addFirst(Node x){
x.next=head.next;
x.pre=head;
head.next.pre=x;
head.next=x;
size++;
}
//删除链表中的x节点(x一定存在)
//由于是双链表且给的是目标Node节点,时间O(1)
public void remove(Node x){
x.pre.next=x.next;
x.next.pre=x.pre;
size--;
}
//删除链表中最后一个节点,并返回该节点,时间O(1)
public Node removeLast(){
if(tail.pre==head){
return null;
}
Node last=tail.pre;
remove(last);
return last;
}
//返回链表长度,时间O(1)
public int size(){return size;}
}
//key 映射到 Node(key,val)
private HashMap<Integer,Node> map;
//Node(k1,k2) 双向链表:k1 - k2 - ...
private DoubleList cache;
//最大容量
private int cap;
public LRUCache(int capacity) {
map=new HashMap<>();
cache=new DoubleList();
this.cap=capacity;
}
/* 将某个key提升为最近使用的 */
private void makeRecently(int key){
Node x=map.get(key);
//先从链表中删除这个节点
cache.remove(x);
//重新插到队头
cache.addFirst(x);
}
/* 添加最近使用的元素 */
private void addRecently(int key,int val){
Node x=new Node(key,val);
//链表头部就是最近使用的元素
cache.addFirst(x);
//别忘了在map中添加key的映射
map.put(key,x);
}
/* 删除某一个key */
private void deleteKey(int key){
Node x=map.get(key);
//从链表中删除
cache.remove(x);
//从map中删除
map.remove(key);
}
/* 删除最久未使用的元素 */
private void removeLeastRecently(){
// 链表尾部的元素就是最久未使用的元素
Node deleteNode=cache.removeLast();
//别忘了从map中删除它的key
int deletedKey=deleteNode.key;
map.remove(deletedKey);
}
public int get(int key) {
if(!map.containsKey(key)){
return -1;
}
//将该数据提升为最近使用的
makeRecently(key);
return map.get(key).value;
}
public void put(int key, int value) {
if(map.containsKey(key)){
//删除旧的数据
deleteKey(key);
//新插入的数据为最近使用的数据
addRecently(key,value);
return ;
}
if(cap==cache.size()){
//删除最久未使用的元素
removeLeastRecently();
}
//添加为最近使用的元素
addRecently(key,value);
}
}
浙公网安备 33010602011771号