1. 常见的缓存淘汰策略:
    先进先出 FIFO
    最少使用LFULeast Frequently Used
    最近最少使用 LRULeast Recently Used
  2.  
  3. 链表定义:
    链表也是线性表的一种,
    数组需要一块连续的内存空间来存储,对内存要求比较高,
    链表恰恰相反,它并不需要一块连续的内存空间,它通过"指针"将一组零散的内存块
    串联起来使用。
  4.  
  5. 最常见的链表结构:
    单链表
    双向链表
    循环链表
  6.  
  7. 用空间换时间:
    当内存空间充足的时候,如果更加追求代码的执行速度,可以选择空间复杂度相对较高、
    但时间复杂度相对很低的算法或数据结构。
  8.  
  9. 链表 vs 数组性能
    数组 链表
    插入删除 O(n) O(1)
    随机访问 O(1) O(n)
  10.  
  11. 如果基于链表实现LRU缓存淘汰算法?
    思路: 维护一个有序单链表,越靠近链表尾部的结点是越早之间访问的数据,
    当有一个新的数据被访问时,从链表头开始顺序遍历链表。
  12.  
  13. 1. 如果此数据之前已经被缓存在链表中,可以遍历得到这个数据对应的结点,
    并将其从原来的位置删除,然后再插入到链表的头部。
  14.  
  15. 2. 如果此数据没有在缓存链表中,又可以分为两种情况:
    如果此时缓存未满,则将此结点直接插入到链表的头部;
    如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
  16.  
  17. 缓存访问时间复杂度: 因为不管缓存有没有满,都需要遍历一遍链表,因此时间复杂度为O(n)
    可以通过"散列表(Hash table)"来记录每个数据的位置,将缓存访问的时间复杂度降低为O(1)
  18.  
  19. 内容小结:
    链表是跟数组"相反"的数据结构,它跟数组一样,也是非常基础、常用的数据结构。
    不过链表比数组稍微复杂。
  20.  
  21. 从普通的单链表衍生出 双向链表、循环链表、双向循环链表
  22.  
  23. 和数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高。
  24.  
  25. 基于Python语言实现的单链表
  1. # 定义链表节点
  2. class Node(object):
  3. def __init__(self, data, n=None):
  4. self.data = data
  5. self.next = n
  6.  
  7. # 定义链表及其增删改查
  8. class LinkList(object):
  9.  
  10. def __init__(self):
  11. # 初始化空链表
  12. self.head = None
  13. self.tail = None
  14. self.length = 0
  15.  
  16. def is_empty(self):
  17. # 判断链表是否为空
  18. return self.length == 0
  19.  
  20. def append(self, dataOrNode):
  21. """
  22. 在尾部添加数据
  23. :param dataOrNode: Data or Node obj
  24. :return: True or None
  25. """
  26. # 判断是一个数据还是Node对象
  27. if isinstance(dataOrNode, Node):
  28. item = dataOrNode
  29. else:
  30. item = Node(dataOrNode)
  31.  
  32. if self.length == 0:
  33. # 判断是一个空链表, 直接赋值
  34. self.head = item
  35. else:
  36. # 将旧尾部节点的next指向新增加的数据
  37. old_tail = self.tail
  38. old_tail.next = item
  39.  
  40. self.tail = item
  41. self.length += 1
  42. return True
  43.  
  44. def delete(self, index):
  45. """
  46. 删除指定位置的数据
  47. :param index: 位置
  48. :return: True or False
  49. """
  50. if self.is_empty():
  51. print("this chain table is empty.")
  52. return False
  53.  
  54. if index < 0 or index >= self.length:
  55. print("error: out of index.")
  56. return False
  57.  
  58. if index == 0:
  59. # 直接删除第一个数据
  60. self.head = self.head.next
  61. self.length -= 1
  62. return True
  63. else:
  64. j = 0
  65. node = self.head
  66. prev = self.head
  67. # 从头开始遍历,遍历到指定位置,然后删除数据
  68. while node.next and j < index:
  69. prev = node
  70. node = node.next
  71. j += 1
  72.  
  73. if j == index:
  74. prev.next = node.next
  75. self.length -= 1
  76. return True
  77. if index == self.length - 1:
  78. self.tail = prev
  79.  
  80. def insert(self, index, dataOrNode):
  81. """
  82. 在指定位置插入数据
  83. :param index: 位置
  84. :param dataOrNode: Data or Node Obj
  85. :return: True or False
  86. """
  87. if self.is_empty():
  88. print("this chain table is empty")
  89. return False
  90.  
  91. if index < 0 or index >= self.length:
  92. print("error: out of index")
  93. return False
  94.  
  95. if isinstance(dataOrNode, Node):
  96. item = dataOrNode
  97. else:
  98. item = Node(dataOrNode)
  99.  
  100. if index == 0:
  101. # 在首部直接插入数据
  102. item.next = self.head
  103. self.head = item
  104. self.length += 1
  105. else:
  106. j = 0
  107. node = self.head
  108. prev = self.head
  109. # 从头开始遍历,遍历到指定位置,然后插入数据
  110. while node.next and j < index:
  111. prev = node
  112. node = node.next
  113. j += 1
  114. if j == index:
  115. item.next = node
  116. prev.next = item
  117. self.length += 1
  118. return True
  119.  
  120. def update(self, index, data):
  121. """
  122. 更新指定位置的数据
  123. :param index: 位置
  124. :param data: 数据
  125. :return: True or False
  126. """
  127. if self.is_empty() or index < 0 or index >= self.length:
  128. print("error: out of index")
  129. return False
  130.  
  131. j = 0
  132. node = self.head
  133. # 从头开始遍历,遍历到指定位置,然后更新数据
  134. while node.next and j < index:
  135. node = node.next
  136. j += 1
  137.  
  138. if j == index:
  139. node.data = data
  140. return True
  141. return False
  142.  
  143. def get_item(self, index):
  144. """
  145. 获取指定位置的数据
  146. :param index:
  147. :return:
  148. """
  149. if self.is_empty() or index < 0 or index >= self.length:
  150. print("error: out of index")
  151. return
  152.  
  153. j = 0
  154. node = self.head
  155. while node.next and j < index:
  156. node = node.next
  157. j += 1
  158.  
  159. if j == index:
  160. return node.data
  161.  
  162. def clear(self):
  163. """
  164. 删除所有数据
  165. :return:
  166. """
  167. self.head = None
  168. self.length = 0
  169. return True
  170.  
  171. def __len__(self):
  172. return self.length
  173.  
  174. def __getitem__(self, item):
  175. # 使用[]获取实例属性 如obj[item], python会自动调用__getitem__方法;
  176. return self.get_item(item)
  177.  
  178. def __setitem__(self, key, value):
  179. # 使用[]设置实例属性 如obj[key] = value, python会自动调用__setitem__方法;
  180. return self.update(key, value)
  181.  
  182. if __name__ == '__main__':
  183. link = LinkList()
  184. for i in range(5):
  185. link.append(i)
  186.  
  187. print("初始化后,链表长度为:", len(link))
  188. for i in range(len(link)):
  189. print("初始化数据:", link.get_item(i))
  190.  
  191. print("删除指定位置数据:", link.delete(0))
  192.  
  193. print("删除指定数据后,链表长度为:", len(link))
  194. for i in range(len(link)):
  195. print("删除后的数据为:", link.get_item(i))
  196.  
  197. print("指定位置插入数据:", link.insert(1, 100))
  198.  
  199. print("插入数据后的链表长度:", len(link))
  200. for i in range(len(link)):
  201. print("插入后的数据:", link.get_item(i))
  202.  
  203. print("更新指定数据", link.update(1, 200))
  204.  
  205. # 更新数据
  206. link[1] = 100
  207.  
  208. # 获取数据
  209. print(link[1])
  1. 基于Go语言实现的单链表
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. )
  6.  
  7. type Object interface {
  8. }
  9.  
  10. // 定义节点
  11. type Node struct {
  12. data Object
  13. next *Node
  14. }
  15.  
  16. // 定义单向链表
  17. type List struct {
  18. head *Node
  19. tail *Node
  20. size uint64
  21. }
  22.  
  23. // 初始化链表
  24. func (list *List) Init() {
  25. (*list).size = // 此时链表是空的
  26. (*list).head = nil // 没有头
  27. (*list).tail = nil // 没有尾
  28. }
  29.  
  30. // 向尾部添加数据
  31. func (list *List) Append(node *Node) bool {
  32. if node == nil {
  33. return false
  34. }
  35.  
  36. // 将尾部的next设置为空
  37. (*node).next = nil
  38.  
  39. // 将新元素放入单链表中
  40. if (*list).size == {
  41. (*list).head = node
  42. } else {
  43. // 将旧尾部数据的next指向新的数据
  44. oldTail := (*list).tail
  45. (*oldTail).next = node
  46. }
  47.  
  48. // 调整尾部位置及链表元素数量
  49. (*list).tail = node // node成为新的尾部
  50. (*list).size ++ // 元素数量增加
  51. return true
  52. }
  53.  
  54. // 插入数据
  55. func (list *List) Insert(i uint64, node *Node) bool {
  56. // 空的节点、索引超出范围和空链表都无法做插入操作
  57. if node == nil || i > (*list).size || (*list).size == {
  58. return false
  59. }
  60.  
  61. if i == {
  62. // 直接排在第一
  63. (*node).next = (*list).head
  64. (*list).head = node
  65. } else {
  66. // 找前一个元素
  67. preItem := (*list).head
  68. for j := ; uint64(j) < i; j++ {
  69. // 数前面i个元素
  70. preItem = (*preItem).next
  71. }
  72. // 原有元素放到新元素后面,新元素放到前一个元素后面
  73. (*node).next = (*preItem).next
  74. (*preItem).next = node
  75. }
  76.  
  77. (*list).size ++
  78. return true
  79. }
  80.  
  81. // 删除元素
  82. func (list *List) Remove(i uint64, node *Node) bool {
  83. if i >= (*list).size {
  84. return false
  85. }
  86. if i == {
  87. node = (*list).head
  88. (*list).head = (*node).next
  89. if (*list).size == {
  90. (*list).tail = nil
  91. }
  92. } else {
  93. preItem := (*list).head
  94. for j := ; uint64(j) < i; j++ {
  95. preItem = (*preItem).next
  96. }
  97. node = (*preItem).next
  98. (*preItem).next = (*node).next
  99.  
  100. if i == ((*list).size - ) {
  101. (*list).tail = preItem
  102. }
  103. }
  104. (*list).size --
  105. return true
  106. }
  107.  
  108. // 获取元素
  109. func (list *List) Get(i uint64) *Node {
  110. if i >= (*list).size {
  111. return nil
  112. }
  113.  
  114. item := (*list).head
  115. for j := ; uint64(j) < i; j++ {
  116. item = (*item).next
  117. }
  118. return item
  119. }
  120.  
  121. func main() {
  122.  
  123. // 初始化长度为100的空链表
  124. var list = List{}
  125. list.Init()
  126. for i := ; i <= ; i++ {
  127. var node = Node{data: i}
  128. list.Append(&node)
  129. }
  130.  
  131. var node = list.Get()
  132. fmt.Printf("Current node position: %d, data: %d\n", node, node.data)
  133. var deleteNode = &Node{}
  134. result := list.Remove(, deleteNode)
  135. fmt.Printf("Delete result: %+v \n", result)
  136.  
  137. var node2 = list.Get()
  138. fmt.Printf("Current node position: %p, data: %d\n", node2, node2.data)
  139.  
  140. newNode := Node{data: }
  141. result2 := list.Insert(, &newNode)
  142. fmt.Printf("Insert result: %+v \n", result2)
  143.  
  144. var node3 = list.Get()
  145. fmt.Printf("Current node position: %p, data: %d\n", node3, node3.data)
  146.  
  147. fmt.Printf("Head: %d, Tail: %d", list.head.data, list.tail.data)
  148. }

数据结构与算法之美 06 | 链表(上)-如何实现LRU缓存淘汰算法的更多相关文章

  1. 链表:如何实现LRU缓存淘汰算法?

    缓存淘汰策略: FIFO:先入先出策略 LFU:最少使用策略 LRU:最近最少使用策略   链表的数据结构: 可以看到,数组需要连续的内存空间,当内存空间充足但不连续时,也会申请失败触发GC,链表则可 ...

  2. 《数据结构与算法之美》 <04>链表(上):如何实现LRU缓存淘汰算法?

    今天我们来聊聊“链表(Linked list)”这个数据结构.学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应用场景,那就是 LRU 缓存淘汰算法. 缓存是一种提高数据读取性能的技术 ...

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

    今天我们来聊聊“链表(Linked list)”这个数据结构.学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应用场景,那就是+LRU+缓存淘汰算法. 缓存是一种提高数据读取性能的技术 ...

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

    一.什么是链表 和数组一样,链表也是一种线性表. 从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构. 链表中的每一个内存块被称为节点Node. ...

  5. 每天一点点之数据结构与算法 - 应用 - 分别用链表和数组实现LRU缓冲淘汰策略

    一.基本概念: 1.什么是缓存? 缓存是一种提高数据读取性能的技术,在硬件设计.软件开发中都有着非广泛的应用,比如常见的CPU缓存.数据库缓存.浏览器缓存等等.   2.为什么使用缓存?即缓存的特点缓 ...

  6. Chapter 6 链表(上):如何实现LRU缓存淘汰算法?

    缓存淘汰策略: 一.什么是链表? 1.和数组一样,链表也是一种线性表. 2.从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构. 3.链表中的每 ...

  7. 详解工程师不可不会的LRU缓存淘汰算法

    大家好,欢迎大家来到算法数据结构专题,今天我们和大家聊一个非常常用的算法,叫做LRU. LRU的英文全称是Least Recently Used,也即最不经常使用.我们看着好像挺迷糊的,其实这个含义要 ...

  8. LRU缓存淘汰算法

    什么是LRU算法? LRU是Least Recently Used的缩写,即最近最少使用,在有限的内容块中存储最近使用次数最多的数据,当内容块已满时,把最少使用的数据删除以便存储新的内容.

  9. 昨天面试被问到的 缓存淘汰算法FIFO、LRU、LFU及Java实现

    缓存淘汰算法 在高并发.高性能的质量要求不断提高时,我们首先会想到的就是利用缓存予以应对. 第一次请求时把计算好的结果存放在缓存中,下次遇到同样的请求时,把之前保存在缓存中的数据直接拿来使用. 但是, ...

随机推荐

  1. What is /dev/null 2>&1?

    >> /dev/null redirects standard output (stdout) to /dev/null, which discards it. (The >> ...

  2. Unity3D学习笔记——NGUI之UITable

    UITable:这个控件可以方便的排列其他小组件,并能控制小组件之间的距离. 效果如下: 这个控件的效果和UIGrid很相似,区别是UIGrid能控制每个小组件的大小,而这个 控件控制的是小组件之前的 ...

  3. 【动态规划】skiing

    [动态规划]skiing 时间限制: 1 Sec  内存限制: 128 MB提交: 34  解决: 15[提交][状态][讨论版] 题目描述 Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激. ...

  4. JS中的动态合集与静态合集

    JS的动态合集 前言 DOM是JavaScript重要组成部分,在DOM中有三个特别的集合分别是NodeList(节点的集合),NamedNodeMap(元素属性的集合)和HTMLCollection ...

  5. JS中的加号+运算符详解

    加号+运算符 在 JavaScript 中,加法的规则其实很简单,只有两种情况: 把数字和数字相加 把字符串和字符串相加 所有其他类型的值都会被自动转换成这两种类型的值. 为了能够弄明白这种隐式转换是 ...

  6. 标准模板库--STL

    标准模板库STL 1.泛型程序设计 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1.面向对象的思想:继承和多态,标准类库 2.泛型程序设计(generic progra ...

  7. debug_backtrace final catch

    <?php function backtrace_str(){ $str = ''; $w = 0; $backtrace = debug_backtrace(); foreach($backt ...

  8. Centos7安装zookeeper

    1.进入/opt cd /opt 2.下载 zookeeper-3.4.10.tar.gz: wget https://mirrors.tuna.tsinghua.edu.cn/apache/zook ...

  9. pmd 使用笔记

    pmd是一块开源的代码静态分析工具,使用java编写,可以自定义规则来进行自己想要的分析.pmd可以单独使用,也可以作为idea.eclipse的插件使用.它的规则分为xpath规则,和java规则. ...

  10. SaltStack任务计划

    编辑fansik_cron.sls文件: 内容如下: cron_test: cron.present: - name: /bin/touch /tmp/fansik.txt - user: root ...