这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
前言
大家好,我是程序猿小白 GW_gw,很高兴能和大家一起学习进步。
以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。
摘要
本文主要带大家了解leetcode第146题LRU缓存的解法。
题目
解法
首先我们先了解一下什么是LRU,有助于理解思路。
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法。 该算法为每个页面创建一个访问字段,记录上次该页面被访问到现在的时间T,当需要淘汰页面时,选择t最大的进行淘汰。
再来看一下题目要求,get时间复杂度要求为O(1),我们不难想到使用hashMap。 put的时间复杂度为O(1),我们可以使用双向链表来实现,靠近头节点是最近使用的,靠近尾节点是最久未使用的。
接着我们来分析get和put操作的实现。
get:
1. 如果key存在,更新key的value,把该节点移动到头节点。
2. 如果key不存在,返回-1.
put:
1. 如果key存在,更新key的value,把该节点移动到头节点。
2. 如果key不存在,创建新节点,并插入到链表头部。接着判断是否超出容量,如果超出容量,删除尾节点。
细节优化:伪造头节点和尾节点这样就不需要判断相邻节点是否存在。
通过上面分析,我们需要经常使用到把节点移动到头部、删除尾节点操作我们可以进行封装尾方法,方便我们调用。
//删除节点
private void removeNode(DLinkedNode dNode){
//该节点的前一个节点指向该节点的后一个节点
dNode.prev.next = dNode.next;
dNode.next.prev = dNode.prev;
}
//在头部添加节点
private void addToHead(DLinkedNode dNode){
dNode.prev = head;
dNode.next = head.next;
dNode.next.prev = dNode;
dNode.next = dNode;
}
//移动节点到头部
private void moveToHead(DLinkedNode dNode){
removeNode(dNode);
addToHead(dNode);
}
//删除尾节点
private DLinkedNode removeTail(){
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
get和put方法实现:
class DLinkedNode{
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {
}
public DLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
private Map<Integer,DLinkedNode> map = new HashMap<>();
private int capacity;
private int size;
private DLinkedNode head,tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
//伪造头节点和尾节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = map.get(key);
if(node==null){
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = map.get(key);
if(node==null){
//如果不包含该key,就添加进去
DLinkedNode newNode = new DLinkedNode(key,value);
map.put(key,newNode);
addToHead(newNode);
size++;
//之后判断是否超出容量
if(size>capacity){
DLinkedNode tail = removeTail();
size--;
map.remove(tail.key);
}
}
else{
//如果包含直接更新,并修改位置
node.value = value;
moveToHead(node);
}
}
小结
以上就是关于Leetcode的146题LRU缓存的解法的一些介绍,希望能对读者有所帮助,如有不正之处,欢迎留言指正。