大家好!我是曾续缘💕
今天是《LeetCode 热题 100》系列
发车第 35 天
链表第 14 题
❤️点赞 👍 收藏 ⭐再看,养成习惯
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。实现LRUCache类:
LRUCache(int capacity)以 正整数 作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字key已经存在,则变更其数据值value;如果不存在,则向缓存中插入该组key-value。如果插入操作导致关键字数量超过capacity,则应该 逐出 最久未使用的关键字。函数
get和put必须以O(1)的平均时间复杂度运行。示例:
输入 ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 输出 [null, null, null, 1, null, -1, null, -1, 3, 4] 解释 LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // 缓存是 {1=1} lRUCache.put(2, 2); // 缓存是 {1=1, 2=2} lRUCache.get(1); // 返回 1 lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3} lRUCache.get(2); // 返回 -1 (未找到) lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} lRUCache.get(1); // 返回 -1 (未找到) lRUCache.get(3); // 返回 3 lRUCache.get(4); // 返回 4提示:
难度:💖💖
1 <= capacity <= 30000 <= key <= 100000 <= value <= 105- 最多调用
2 * 105次get和put
解题方法
这道题目需要设计并实现一个满足LRU(最近最少使用)缓存约束的数据结构。为了实现LRU缓存,我们可以采用哈希表和双向链表的组合来解决问题。
我们使用哈希表来快速查找关键字所对应的缓存节点,哈希表的键是关键字,值是节点指针。这样可以以O(1)的时间复杂度快速找到某个关键字对应的节点。
为了维护缓存的最近访问顺序,我们使用双向链表。双向链表中的节点按照访问顺序从头到尾依次排列,头部是最近访问过的节点,尾部是最久未被访问的节点。这样,当节点被访问时,我们可以快速将它移到链表的头部,而当需要淘汰节点时,可以直接删除尾部节点。
我们使用head和tail节点来简化边界条件的处理,它们不存储任何数据,只作为辅助节点。插入新节点时,我们将新节点插入到head节点之后,删除节点时,我们删除tail节点之前的节点。
Code
查看代码
java
class LRUCache {
class Node{
int key;
int value;
Node prev;
Node next;
public Node(){key = -1;};
public Node(int k, int v){
key = k;
value = v;
}
}
private Map<Integer, Node> mp = new HashMap<Integer, Node>();
private int capacity;
private Node head, tail;
public LRUCache(int capacity) {
this.capacity = capacity;
head = new Node();
tail = new Node();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if(mp.containsKey(key)){
Node node = mp.get(key);
node.prev.next = node.next;
node.next.prev = node.prev;
moveToHead(node);
return node.value;
}else{
return -1;
}
}
public void put(int key, int value) {
if(mp.containsKey(key)){
Node node = mp.get(key);
node.value = value;
node.prev.next = node.next;
node.next.prev = node.prev;
moveToHead(node);
}else{
Node node = new Node(key, value);
mp.put(key, node);
if(mp.size() > capacity){
Node delNode = tail.prev;
mp.remove(delNode.key);
tail.prev.prev.next = tail;
tail.prev = tail.prev.prev;
}
moveToHead(node);
}
}
private void moveToHead(Node node){
head.next.prev = node;
node.next = head.next;
node.prev = head;
head.next = node;
}
}
/**
* 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);
*/查看代码
go
type Node struct {
key, value int
pre, nxt *Node
}
type LRUCache struct {
capacity int
dummy *Node
mp map[int]*Node
}
func Constructor(capacity int) LRUCache {
dummy := &Node{}
dummy.pre, dummy.nxt = dummy, dummy
return LRUCache{
capacity: capacity,
dummy: dummy,
mp: map[int]*Node{},
}
}
func (this *LRUCache) unlink(cur *Node) {
cur.pre.nxt, cur.nxt.pre = cur.nxt, cur.pre
cur.pre, cur.nxt = nil, nil
}
func (this *LRUCache) tohead(cur *Node) {
cur.pre = this.dummy
cur.nxt = this.dummy.nxt
this.dummy.nxt.pre = cur
this.dummy.nxt = cur
}
func (this *LRUCache) Get(key int) int {
node := this.mp[key]
if node == nil {
return -1
}
this.unlink(node)
this.tohead(node)
return node.value
}
func (this *LRUCache) Put(key int, value int) {
node := this.mp[key]
if node == nil {
node = &Node{key: key, value: value}
this.mp[key] = node
} else {
this.unlink(node)
node.value = value
}
this.tohead(node)
if len(this.mp) > this.capacity {
tail := this.dummy.pre
delete(this.mp, tail.key)
this.unlink(tail)
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/