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

186 阅读2分钟

五花八门的链表结构

  • 链表的主要应用:LRU 缓存淘汰算法

  • 缓存:为了提升读性能的组件,通常用于高速存储中

    • 缓存的栗子:CPU 缓存、数据库缓存、浏览器缓存等等

    • 缓存淘汰策略:

      • 先进先出策略 FIFO   使用队列\

      • 最少使用策略 LFU    有序队列\

      • 最近最少使用策略 LRU  链表\

  • 常见的链表:单链表、双向链表和循环链表

  • 链表组成:节点+后继指针

    • 头结点 记录基地址
    • 尾节点 指向一个空地址
  • 单链表

    • 优点:在链表中插入和删除元素的时间复杂度都是o(1) 这点优于数组
    • 缺点:不支持随机访问,查询复杂度o(n)
  • 循环链表

    • 与单链表的区别是 循环链表的尾节点指向链表的头节点
    • 简洁的完成约瑟夫问题 todo
  • 双向链表

    • 有后继指针和前继指针

    • 相比于单链表增加向前指针,但是支持双向遍历

    • 特点

      • 双向链表可以支持 O(1) 时间复杂度的情况下找到前驱结点,在插入和删除都会更加简单(不需要两个指针)可以直接获取前继节点\

      • 还可以从临时元素开始向前向后查找

    • Java中的LinkedHashMap  空间换时间的思想 类似的还有缓存

  • 双向循环链表

\

链表 VS 数组性能大比拼

数组链表
连续的内存空间可以利用CPU的缓存机制,预读数据零散的内存块
固定大小,无法利用内存碎片可以利用内存碎片
更加节省内存空间增加内存空间,造成内存碎片,可能导致频繁的GC

\

\

解答开篇

  • 如何利用链表实现一个有序单列表

    • 维护一个有序链表,头部节点是新访问的,尾部节点是最早访问的
    • 新增一个元素,如果在队列中则更新到头部,否则直接插入头部
    • 如果缓存已满,则删除尾部元素
    • 缓存访问的时间复杂度为 O(n)\
  • 引入散列表将访问复杂度降到O(1)

总结

  • 链表:单链表,双向链表,循环链表,双向循环链表
  • 和数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高