当前位置: 首页 > news >正文

使用 Swift 实现 LRU 缓存淘汰策略

📌 实现思路

一、核心目标

我们要实现一个缓存类:

  • 支持通过 get(key) 获取缓存的值;
  • 支持通过 put(key, value) 写入缓存;
  • 缓存容量有限,当超过容量时要淘汰最久未使用的元素

二、为什么用「哈希表 + 双向链表」

功能使用的结构原因
快速查找 key哈希表 (dict)O(1) 时间复杂度
快速移动元素到头部双向链表O(1) 移除 / 插入节点,无需整体移动元素
快速删除最旧元素链表尾部淘汰尾节点指针指向最久未使用项,删除也只需 O(1) 操作

🧾 完整代码 + 注释

// 声明一个通用的 LRU 缓存类,Key 必须是 Hashable 的,Value 任意类型
class LRUCache<Key: Hashable, Value> {

    // 内部节点类:用于双向链表的节点结构
    private class Node {
        let key: Key
        var value: Value
        var prev: Node?
        var next: Node?

        init(key: Key, value: Value) {
            self.key = key
            self.value = value
        }
    }

    private let capacity: Int                        // 最大缓存容量
    private var dict: [Key: Node] = [:]              // 哈希表,用于快速访问节点
    private var head: Node?                          // 链表头(最近使用)
    private var tail: Node?                          // 链表尾(最久未使用)

    // 初始化函数
    init(capacity: Int) {
        self.capacity = capacity
    }

    // 读取缓存
    func get(_ key: Key) -> Value? {
        guard let node = dict[key] else {
            return nil // 如果找不到,返回 nil
        }

        moveToHead(node) // 将访问的节点移到头部,表示“最近被使用”
        return node.value
    }

    // 写入缓存
    func put(_ key: Key, value: Value) {
        if let node = dict[key] {
            // 已存在,更新值并移到头部
            node.value = value
            moveToHead(node)
        } else {
            // 新节点,插入到头部
            let newNode = Node(key: key, value: value)
            dict[key] = newNode
            addToHead(newNode)

            // 如果超过容量,移除尾部最久未使用的节点
            if dict.count > capacity {
                if let removed = removeTail() {
                    dict.removeValue(forKey: removed.key)
                }
            }
        }
    }

    // 添加节点到链表头部
    private func addToHead(_ node: Node) {
        node.next = head
        node.prev = nil
        head?.prev = node
        head = node
        if tail == nil {
            tail = head
        }
    }

    // 从链表中移除某个节点
    private func removeNode(_ node: Node) {
        if let prev = node.prev {
            prev.next = node.next
        } else {
            head = node.next // node 是头部
        }

        if let next = node.next {
            next.prev = node.prev
        } else {
            tail = node.prev // node 是尾部
        }
    }

    // 将某个节点移到头部(表示最近使用)
    private func moveToHead(_ node: Node) {
        removeNode(node)
        addToHead(node)
    }

    // 移除尾部节点(最久未使用的)
    private func removeTail() -> Node? {
        guard let oldTail = tail else { return nil }
        removeNode(oldTail)
        return oldTail
    }
}

📈 使用示例(调试输出)

let cache = LRUCache<String, Int>(capacity: 2)
cache.put("a", value: 1)
cache.put("b", value: 2)
print(cache.get("a") ?? -1)  // 输出 1 ("a" 成为最近使用)
cache.put("c", value: 3)     // 淘汰 "b",因为 "b" 是最久未使用的
print(cache.get("b") ?? -1)  // 输出 -1(已被淘汰)

🔍 运行原理图解

每次执行操作时,双向链表的结构如下所示(假设 head 在左,tail 在右):

  • 初始放入 ahead = a, tail = a
  • 放入 ba <-> b
  • 访问 aa 移到头部 → a <-> b
  • 放入 c,超过容量,淘汰尾部 ba <-> c

✅ 总结亮点

特性实现方式
泛型支持<Key: Hashable, Value> 泛型设计
O(1) 查找使用 Dictionary
O(1) 插入删除使用双向链表
高复用性泛型设计支持任意 Key/Value 类型

http://www.dtcms.com/a/111164.html

相关文章:

  • React编程模型:Project Reactor深度解析
  • Java的基本语法
  • 006贪心——算法备赛
  • 蓝桥杯 2023 省赛 B 组 E 题。
  • KubeVirt虚拟化管理架构
  • 热更新简介+xLua基础调用
  • 《AI大模型开发笔记》MCP快速入门实战(一)
  • 英语学习:读科技论文的难处
  • JSON介绍
  • 免费送源码:Java+ssm+MySQL SpringBoot社区配送服务系统小程序 计算机毕业设计原创定制
  • OpenMinus 源码深度解析:从 React 模式到多智能体架构实现
  • 元宇宙浪潮下,前端开发如何“乘风破浪”?
  • OCR的备份与恢复
  • Nacos 如何管理配置版本?支持配置回滚吗?如何实现?
  • 栈回溯和离线断点
  • 2024第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
  • 4.4 代码随想录第三十五天打卡
  • 生活电子常识--删除谷歌浏览器搜索记录
  • 家里网络访问Github有时候打不开,解决办法
  • kotlin中const 和val的区别
  • 算法刷题记录——LeetCode篇(3.3) [第221~230题](持续更新)
  • Linux环境下内存错误问题排查与修复
  • Mysql 中 ACID 背后的原理
  • 状态机思想编程
  • 《微服务》概念详解
  • MINIQMT学习课程Day7
  • Github 2025-04-04Java开源项目日报 Top8
  • AIP-213 通用组件
  • 【动态规划】深入动态规划:连续子结构的算法剖析
  • 人工智能:RNN和CNN详细分析