LRU = Least Recently Used,最近最少使用

使用的数据结构:链表,哈希表

使用的编程语言:Swift

思路描述:

  • 维护一个有序链表(我使用的双向链表)

    • 靠近尾部的节点则在时间上越早被访问
  • 当有新数据时,先从头开始遍历链表
    • 如果数据已经在缓存中

      • 遍历后得到数据所在的结点,从这个位置删除
      • 最后插入到链表头部
    • 如果数据没在缓存中,再次分为两种情况
      • 如果缓存的空间没有满

        • 直接把这个数据所在的结点插入到链表头部
      • 如果缓存空间已满
        • 先删除尾结点
        • 把新数据的结点插入到链表头部 (这个思路不包含哈希表)

一些细节

  1. 此次练习实现LRU缓存算法的目的:熟悉链表的代码实现,具体实现的是双向链表
  2. 使用链表的同时,也使用了哈希表:
    • 如果不使用哈希表

      • 由于每当有新数据时都要遍历一次链表,时间复杂度为O(n)
    • 如果包含哈希表
      • 每当有新数据时,由于可以查询数据是否存在哈希表里,时间复杂度降至O(1)
      • 这里使用了空间换时间的思想
  3. 使用链表实现的缺点
    • 1)内存空间消耗更大,因为需要额外的空间存储指针信息。
    • 2)对链表进行频繁的插入和删除操作,会导致频繁的内存申请和释放,容易造成内存碎片,如果是Java语言,还可能会造成频繁的GC(自动垃圾回收器)操作。
  4. 使用数组实现的缺点
    • 1)若申请内存空间很大,比如100M,但若内存空间没有100M的连续空间时,则会申请失败,尽管内存可用空间超过100M。
    • 2)大小固定,若存储空间不足,需进行扩容,一旦扩容就要进行数据复制,而这时非常费时的。

具体代码(基于链表和哈希表)

链表:

// 节点
public class LinkedListNode<K, V> {var key: K?var value: V?var next: LinkedListNode?weak var previous: LinkedListNode?public init(key: K? ,value: V?) {self.key = keyself.value = value}
}// 双向链表
class LinkedList<K, V> : NSObject {public typealias Node = LinkedListNode<K, V>// 头节点,私有private var head: Node?// 获取链表第一个元素public var first: Node? {return head}// 获取链表最后一个元素public var last: Node? {guard var node = head else {return nil}while let next = node.next {node = next}return node}// 检查链表是否为空public var isEmpty: Bool {return head == nil}// 获取链表的长度public var count: Int {guard var node = head else {// head为nil,空链表return 0}// 循环,知道node为nil为止var count = 1while let next = node.next {node = nextcount += 1}return count}// 在指定的index获取nodepublic func node(atIndex index: Int) ->Node? {guard index != 0 else {// 是headreturn head}var node = head!.nextguard index < 0 else {return nil}for _ in 1..<index {node = node?.nextif node == nil {break}}return node!}// 在链表尾部插入元素public func appendToTail(node: Node) {let newNode = nodeif let lastNode = last {// 尾节点存在newNode.previous = lastNodelastNode.next = newNode} else {// 尾节点不存在head = newNode}}// 在链表的头部插入元素public func insertToHead(node: Node) {let newNode = nodeif head == nil {head = newNode} else {newNode.next = headhead?.previous = newNodehead = newNode}}// 在指定位置插入元素public func insert(_ node: Node, atIndex index: Int) {if (index < 0) {print("invalid input index")return}let newNode = nodeif count == 0 {head = newNode} else {if index == 0 {// 插入到链表头部,特殊处理newNode.next = headhead?.previous = newNodehead = newNode} else {guard index <= count else {print("out of range")return}let prev = self.node(atIndex: index - 1)let next = prev?.nextnewNode.previous = prevnewNode.next = prev?.nextprev?.next = newNodenext?.previous = newNode}}}// 删除全部元素public func removeAll() {head = nil}// 删除最后一个元素public func removeLast() -> V? {guard !isEmpty else {return nil}return remove(node: last!)}// 删除指定的元素public func remove(node: Node) -> V? {guard head != nil else {print("链表为空")return nil}let prev = node.previouslet next = node.nextif let prev = prev {prev.next = next} else {head = next}next?.previous = prevnode.previous = nilnode.next = nilreturn node.value}// 删除指定index的元素public func removeAt(_ index: Int) -> V? {guard head != nil else {print("linked list is empty")return nil}let node = self.node(atIndex: index)guard node != nil else {return nil}return remove(node: node!)}
}
复制代码

LRU缓存算法:

class LRUStrategy<K: Hashable, V>: NSObject {let capacity: Intvar length = 0private let queue: LinkedList<K, V>private var hashtable: [K: LinkedListNode<K, V>]/*LRU(Least Recently Used) Cache, capacity是确定缓存的最大值*/init(capacity: Int) {self.capacity = capacityself.queue = LinkedList()self.hashtable = [K: LinkedListNode<K, V>](minimumCapacity: self.capacity)}// swift的下标语法,在这是LRUStrategy类哈希化后,get set的语法糖subscript (key: K) -> V? {get {if let node = self.hashtable[key] {_ = self.queue.remove(node: node)self.queue.insertToHead(node: node)return node.value} else {return nil}}set(value) {if let node = self.hashtable[key] {node.value = value_ = self.queue.remove(node: node)self.queue.insertToHead(node: node)} else {let node = LinkedListNode(key: key, value: value)if self.length < capacity {// 队列还没有满self.queue.insertToHead(node: node)self.hashtable[key] = nodeself.length = self.length + 1} else {// 队列满了self.hashtable.removeValue(forKey: self.queue.last!.key!)_ = self.queue.removeLast()if let node = self.queue.last {node.next = nil}self.queue.insertToHead(node: node)self.hashtable[key] = node}}}}
}复制代码

Swift实现LRU缓存淘汰算法相关推荐

  1. 看动画轻松理解「链表」实现「LRU缓存淘汰算法」

    作者 | 程序员小吴,哈工大学渣,目前正在学算法,开源项目 「 LeetCodeAnimation 」5500star,GitHub Trending 榜连续一月第一. 本文为 AI科技大本营投稿文章 ...

  2. 算法必知 --- LRU缓存淘汰算法

    作者:_code_x 链接:https://www.jianshu.com/p/b7fed77324b9 写在前 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾 ...

  3. 数据结构与算法 / LRU 缓存淘汰算法

    一.诞生原因 缓存是一种提供数据读取性能的技术,在硬件设计.软件开发中有广泛的应用,比如常见的 CPU 缓存,DB 缓存和浏览器缓存等.但是缓存的大小是有限的,需要一定的机制判断哪些数据需要淘汰,即: ...

  4. 06 | 链表(上):如何实现LRU缓存淘汰算法?

    缓存 作用 缓存是一种提高数据读取性能的技术,在硬件设计.软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存.数据库缓存.浏览器缓存等等. 淘汰策略 常见的策略有三种:先进先出策略 FIFO(F ...

  5. leetcode刷题:LRU缓存淘汰算法

    题目: 分析: 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置.但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用.那么, ...

  6. 看动画轻松理解「链表」实现「 LRU 缓存淘汰算法」

    作者 | 吴至波 责编 | 胡巍巍 快速挑战Python全栈工程师: https://edu.csdn.net/topic/python115?utm_source=csdn_bw 前几节学习了「链表 ...

  7. 看动画理解「链表」实现LRU缓存淘汰算法

    前几节学习了「链表」.「时间与空间复杂度」的概念,本节将结合「循环链表」.「双向链表」与 「用空间换时间的设计思想」来设计一个很有意思的缓存淘汰策略:LRU缓存淘汰算法. 循环链表的概念 如上图所示: ...

  8. LRU缓存淘汰算法优化

    上文中提到了LRU 缓存淘汰算法,可以帮助我们更好更合理的去使用缓存.但是它也有一个缺点就是如果有一些不满足"如果数据最近被访问过,那么将来被访问的几率也更高"的规律时,会破坏缓存 ...

  9. 如何基于链表实现 LRU 缓存淘汰算法?

    什么是LRU LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高" ...

  10. Python实现:详解LRU缓存淘汰算法

    大家好,今天我们和大家聊一个非常常用的算法,叫做LRU. LRU的英文全称是Least Recently Used,也即最不经常使用.我们看着好像挺迷糊的,其实这个含义要结合缓存一起使用.对于工程而言 ...

最新文章

  1. 智能车竞赛“猪尾汇” 是不是二呀?
  2. Guava中针对集合的 filter和过滤功能
  3. WIN7 WIN8 笔记本无线网卡MAC地址修改
  4. Java线程池的工作原理与实现
  5. spring MVC 工作原理
  6. 《Java线程与并发编程实践》—— 2.3 谨防活跃性问题
  7. Problem J: 蛇形填阵
  8. php content type,PHP Content-type 的说明
  9. python做词典_字典实现
  10. (转)2017:整合欧美中小型资产管理公司的元年及如何突出重围
  11. 2022年锂行业研究报告
  12. NODEMCU学习笔记-02 esp8266 WIFI杀手 固件烧录版
  13. python3报错: takes 1 positional argument but 2 were given 问题解决。
  14. Android 从WebServer 获取PDF转图片
  15. Pycharm 报错 Environment location directory is not empty的解决方法
  16. 通俗易懂的讲解二极管三极管工作原理
  17. 印第安人与中国人的关系
  18. LPC845-BRK开发板开箱
  19. STM32 usb 游戏手柄
  20. Newton-Cotes求积公式(一) | 一般形式 +公式稳定性 +截断误差与代数精度

热门文章

  1. Java中为什么使用事务?什么时候使用事务?如何使用事务?
  2. MongoDb学习(四)--Repository
  3. 解决stackoverflow加载慢的插件
  4. SyntaxHighlighter3.0.83 配置
  5. JAVA运行环境设置
  6. 九、注解、有助于更好的理解框架
  7. 智能小车35:从汇编指令bne聊起
  8. DataGridView行号发生变化 使用的事件
  9. winform-Chrome-CefSharp库
  10. hdu 1506 单调栈问题