一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
前言
力扣第146题 LRU 缓存 如下所示:
一、思路
题目很长,但题目的目的很简单:自己实现一个满足 LRU 的数据结构。LRU缓存机制是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
此外题目中还有一个要求:get 和 put 操作都需要满足 O(1) 的时间复杂度
如何实现这一要求呢?我么在这里将 get 和 put 操作分开看。
get操作:为了满足取元素时,时间复杂度为O(1)。可以使用一个哈希表HashMap来存储所有最近使用的元素。如果哈希表中存在key则返回对应的val即可put操作会有插入和更新两种情况,为了在插入和更新时都能有O(1)的时间复杂度,我们可以使用双向链表来存储最近的元素。- 插入:插入时需要判断当前缓存的大小是否会溢出,如不溢出则直接插入到头部即可,如溢出则移除最后一个元素后,再将元素插入到头部
- 更新:更新元素的值后,将当前元素移动到头部
双向链表
假设有如下的双向链表,有 a, b, c 三个节点:
我们缓存的大小设置为 3,也就是说双向链表中存储的元素不要超过 3
- 更新
假设我们需要更新 b 节点,用黄色标识。
我们现在双向链表中移除节点 b
然后再将 b 放到双向链表的头部
- 插入新元素
假设插入的元素为 d,我们先移除双向链表中的最后一个元素 c
再将 d 放到双向链表的头部即可
二、实现
实现代码
实现代码中主要就是需要自己手写一个双向链表 DoubleLinkNode,此外要注意,最近使用的元素放到链表的头部。
public class LRUCache {
class DoubleLinkNode {
int key;
int val;
DoubleLinkNode before;
DoubleLinkNode next;
public DoubleLinkNode() {}
public DoubleLinkNode(int key, int val) {
this.key = key;
this.val = val;
}
}
private Map<Integer, DoubleLinkNode> map = new HashMap<>();
private int size;
private int capacity;
private DoubleLinkNode head, tail;
// 初始化
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 前置和后置节点
head = new DoubleLinkNode();
tail = new DoubleLinkNode();
head.next = tail;
tail.before = head;
}
public int get(int key) {
DoubleLinkNode node = map.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
DoubleLinkNode node = map.get(key);
if (node == null) {
if (size == capacity) {
// 移除尾部节点
DoubleLinkNode tail = removeTail();
map.remove(tail.key);
size--;
}
DoubleLinkNode newNode = new DoubleLinkNode(key, value);
map.put(key, newNode);
addToHead(newNode);
++size;
}
else {
// 移动到头部
node.val = value;
moveToHead(node);
}
}
private void addToHead(DoubleLinkNode node) {
node.before = head;
node.next = head.next;
head.next.before = node;
head.next = node;
}
private void removeNode(DoubleLinkNode node) {
node.before.next = node.next;
node.next.before = node.before;
}
private void moveToHead(DoubleLinkNode node) {
removeNode(node);
addToHead(node);
}
private DoubleLinkNode removeTail() {
DoubleLinkNode res = tail.before;
removeNode(res);
return res;
}
}
测试代码
本题无测试代码
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~