LeetCode - #146 LRU 缓存(Top 100)
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
前言
本题为 LeetCode 前 100 高频题
我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。
LeetCode 算法到目前我们已经更新到 145 期,我们会保持更新时间和进度(周一、周三、周五早上 9:00 发布),每期的内容不多,我们希望大家可以在上班路上阅读,长久积累会有很大提升。
不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。
难度水平:中等
1. 描述
请你设计并实现一个满足 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)
的平均时间复杂度运行。
2. 示例
示例 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 <= 10^5
- 最多调用
2 * 10^5
次get
和put
3. 答案
```swift class LRUCache {
private let capacity: Int
private var count = 0
private let head = LRUCacheNode(0, 0)
private let tail = LRUCacheNode(0, 0)
private var dict = [Int: LRUCacheNode]()
init(_ capacity: Int) {
self.capacity = capacity
head.next = tail
tail.pre = head
}
func get(_ key: Int) -> Int {
if let node = dict[key] {
remove(key)
insert(node)
return node.val
}
return -1
}
func put(_ key: Int, _ value: Int) {
if let node = dict[key] {
node.val = value
remove(key)
insert(node)
return
}
let node = LRUCacheNode(key, value)
dict[key] = node
if count == capacity, let tailKey = tail.pre?.key {
remove(tailKey)
}
insert(node)
}
private func insert(_ node: LRUCacheNode) {
dict[node.key] = node
node.next = head.next
head.next?.pre = node
node.pre = head
head.next = node
count += 1
}
private func remove(_ key: Int) {
guard count > 0, let node = dict[key] else {
return
}
dict[key] = nil
node.pre?.next = node.next
node.next?.pre = node.pre
node.pre = nil
node.next = nil
count -= 1
}
}
fileprivate class LRUCacheNode {
let key: Int
var val: Int
var pre: LRUCacheNode?
var next: LRUCacheNode?
init(_ key: Int, _ val: Int) {
self.key = key
self.val = val
}
} ```
- 主要思想:使用链表和哈希映射来构建缓存。
- 时间复杂度: O(1)
- 空间复杂度: O(k)
该算法题解的仓库:LeetCode-Swift
点击前往 LeetCode 练习
关于我们
我们是由 Swift 爱好者共同维护,我们会分享以 Swift 实战、SwiftUI、Swift 基础为核心的技术内容,也整理收集优秀的学习资料。
后续还会翻译大量资料到我们公众号,有感兴趣的朋友,可以加入我们。
- 在 SwiftUI 中创建一个环形 Slider
- Swift 周报 第二十五期
- Swift 周报 第二十四期
- 在 iOS 16 中用 SwiftUI Charts 创建一个折线图
- Swift 中的 async/await ——代码实例详解
- Swift AsyncSequence — 代码实例详解
- Swift 周报 第十期
- SwiftUI 之 HStack 和 VStack 的切换
- 第三方库并不是必须的
- Swift 周报 第十二期
- LeetCode - #146 LRU 缓存(Top 100)
- LeetCode - #145 二叉树的后序遍历
- 现今 Swift 包中的二进制目标
- LeetCode - #125 验证回文串
- 解决 iOS 15 上 APP 莫名其妙地退出登录
- 用 SwiftLint 保持 Swift 风格一致
- TCA - SwiftUI 的救星?(一)
- Swift 中的热重载
- 在 Swift 中编写脚本:Git Hooks
- LeetCode - #124 二叉树中的最大路径和(Top 100)