题目描述
请你设计并实现一个满足 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 <= 3000
- 0 <= key <= 10000
- 0 <= value <= 105
- 最多调用 2 * 10^5^ 次 get 和 put
代码及注释
go
type entry struct {
key, value int
}
type LRUCache struct {
capacity int // 缓存容量
list *list.List // 双向链表,用于存储最近使用的键值对
keyToNode map[int]*list.Element // 哈希表,用于快速查找键对应的节点
}
// 构造函数
func Constructor(capacity int) LRUCache {
return LRUCache{
capacity: capacity,
list: list.New(),
keyToNode: make(map[int]*list.Element),
}
}
// 获取键值
func (c *LRUCache) Get(key int) int {
// 从哈希表中查找键对应的节点
node := c.keyToNode[key]
if node == nil {
return -1
}
// 将使用过的节点移动到链表头部
c.list.MoveToFront(node)
return node.Value.(entry).value
}
// 存储键值
func (c *LRUCache) Put(key, value int) {
// 如果键已存在,则更新其值,并将节点移动到链表头部
if node := c.keyToNode[key]; node != nil {
node.Value = entry{key, value}
c.list.MoveToFront(node)
return
}
// 如果缓存已满,则删除链表尾部的节点
c.keyToNode[key] = c.list.PushFront(entry{key, value})
if len(c.keyToNode) > c.capacity {
tail := c.list.Back()
delete(c.keyToNode, tail.Value.(entry).key)
c.list.Remove(tail)
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/
代码解释
-
结构体定义:
gotype entry struct { key, value int } type LRUCache struct { capacity int list *list.List keyToNode map[int]*list.Element }
entry
结构体用于存储键值对。LRUCache
结构体包含缓存的容量、双向链表和一个哈希表。
-
构造函数:
gofunc Constructor(capacity int) LRUCache { return LRUCache{ capacity: capacity, list: list.New(), keyToNode: make(map[int]*list.Element), } }
- 初始化一个
LRUCache
实例,并设置容量、双向链表和哈希表。
- 初始化一个
-
获取键值:
gofunc (c *LRUCache) Get(key int) int { node := c.keyToNode[key] if node == nil { return -1 } c.list.MoveToFront(node) return node.Value.(entry).value }
- 从哈希表中查找给定的键。
- 如果键不存在,返回 -1。
- 如果键存在,将对应的节点移动到链表头部,并返回值。
-
存储键值:
gofunc (c *LRUCache) Put(key, value int) { if node := c.keyToNode[key]; node != nil { node.Value = entry{key, value} c.list.MoveToFront(node) return } c.keyToNode[key] = c.list.PushFront(entry{key, value}) if len(c.keyToNode) > c.capacity { tail := c.list.Back() delete(c.keyToNode, tail.Value.(entry).key) c.list.Remove(tail) } }
- 检查给定的键是否已存在于哈希表中。
- 如果键存在,更新其值,并将对应的节点移动到链表头部。
- 如果键不存在,将新的键值对存入哈希表,并将对应的节点插入链表头部。
- 如果缓存容量超过了限制,删除链表尾部的节点,并从哈希表中删除对应的键值对。
这个LRU Cache的实现使用了双向链表和哈希表来实现高效的插入、删除和更新操作,时间复杂度为 O(1)。