力扣链接:146. LRU 缓存机制

思路:哈希表 + 双向链表

为什么必须要用双向链表?

因为我们需要删除操作。删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度 O(1)。

为什么要在链表中同时存储 key 和 val,而不是只存储 val?

当缓存容量已满,我们不仅仅要删除最后一个节点,还要把哈希表 中映射到该节点的 key 同时删除,而这个 key 只能由 节点得到。如果 节点结构中只存储 val,那么我们就无法得知 key 是什么,就无法删除 哈希表中的键,造成错误。

代码

我这里是向尾部添加数据,所以头部的是不活跃的数据值

  1. type LRUCache struct { //LRU 缓存结构
  2. capacity int // 容量
  3. m map[int]*Node //哈希表
  4. cache *NodeList //双向链表
  5. }
  6. type Node struct{ //节点结构
  7. key int
  8. value int
  9. prev *Node //前一个节点
  10. next *Node //后一个节点
  11. }
  12. func initNode(key,value int)*Node{ //初始化节点
  13. return &Node{
  14. key:key,
  15. value:value,
  16. }
  17. }
  18. type NodeList struct{ //链表结构
  19. head *Node //链表头节点
  20. last *Node //链表尾节点
  21. size int //元素个数
  22. }
  23. func initList ()*NodeList{ //初始化链表
  24. dil:=&NodeList{
  25. head:initNode(0,0),
  26. last:initNode(0,0),
  27. size:0,
  28. }
  29. dil.head.next=dil.last
  30. dil.last.prev=dil.head
  31. return dil
  32. }
  33. func (this *NodeList)addNodeinlist(node *Node){ //向链表中插入节点,向链表尾部插节点
  34. node.prev=this.last.prev
  35. this.last.prev=node
  36. node.prev.next=node
  37. node.next=this.last
  38. this.size++
  39. }
  40. func (this *NodeList)deleteNodeinlist (node *Node){ //删除链表中的某一结点
  41. node.prev.next=node.next
  42. node.next.prev=node.prev
  43. node.next=nil
  44. node.prev=nil
  45. this.size--
  46. }
  47. func (this *NodeList)delfirstNode()*Node{ //删除第一个节点,并且返回
  48. if this.head.next==this.last{
  49. return nil
  50. }
  51. t:=this.head.next
  52. this.deleteNodeinlist(t)
  53. return t
  54. }
  55. func Constructor(capacity int) LRUCache { //初始化 LRU 缓存
  56. return LRUCache{
  57. capacity:capacity,
  58. m:make(map[int]*Node,0),
  59. cache:initList(),
  60. }
  61. }
  62. func (this *LRUCache)addkey(key,value int){ //添加元素
  63. node:=initNode(key,value)
  64. //增加map映射
  65. this.m[key]=node
  66. //在链表中添加元素
  67. this.cache.addNodeinlist(node)
  68. }
  69. func (this *LRUCache)makekey(key int){ // 将某个 key 调整为最近使用的元素
  70. //找到节点
  71. node:=this.m[key]
  72. //删除节点
  73. this.cache.deleteNodeinlist(node)
  74. // 添加到链表尾部
  75. this.cache.addNodeinlist(node)
  76. }
  77. func (this *LRUCache)deletekey(key int){ //删除元素
  78. //删除链表中节点
  79. this.cache.deleteNodeinlist(this.m[key])
  80. //删除map映射
  81. delete(this.m,key)
  82. }
  83. func (this *LRUCache)deletefirkey(){ //删除最久未使用的元素
  84. // 链表的第一个就是最近最少使用的元素
  85. node:=this.cache.delfirstNode()
  86. // 删除映射
  87. delete(this.m,node.key)
  88. }
  89. func (this *LRUCache) Get(key int) int {
  90. if _,ok:=this.m[key];ok{
  91. //存在
  92. this.makekey(key) //将某个 key 调整为最近使用的元素
  93. return this.m[key].value
  94. }else{
  95. //不存在
  96. return -1
  97. }
  98. }
  99. func (this *LRUCache) Put(key int, value int) {
  100. // 检查key存不存在
  101. if _,ok:=this.m[key];ok{
  102. //存在
  103. //删除元素
  104. this.deletekey(key)
  105. //添加元素到尾部
  106. this.addkey(key,value)
  107. }else{
  108. //不存在
  109. if this.capacity==this.cache.size{
  110. //缓存达到上限
  111. //删除最久未使用的元素
  112. this.deletefirkey()
  113. }
  114. //添加元素到尾部
  115. this.addkey(key,value)
  116. }
  117. }

参考:

https://leetcode-cn.com/problems/lru-cache/solution/jian-dan-shi-li-xiang-xi-jiang-jie-lru-s-exsd/

【golang必备算法】 Letecode 146. LRU 缓存机制的更多相关文章

  1. Java实现 LeetCode 146 LRU缓存机制

    146. LRU缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - ...

  2. 力扣 - 146. LRU缓存机制

    目录 题目 思路 代码 复杂度分析 题目 146. LRU缓存机制 思路 利用双链表和HashMap来解题 看到链表题目,我们可以使用头尾结点可以更好进行链表操作和边界判断等 还需要使用size变量来 ...

  3. 146. LRU 缓存机制 + 哈希表 + 自定义双向链表

    146. LRU 缓存机制 LeetCode-146 题目描述 题解分析 java代码 package com.walegarrett.interview; /** * @Author WaleGar ...

  4. [Leetcode]146.LRU缓存机制

    Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...

  5. 【力扣】146. LRU缓存机制

    运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果关键字 (key) ...

  6. 146. LRU缓存机制

    题目描述 运用你所掌握的数据结构,设计和实现一个LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (key ...

  7. Leetcode 146. LRU 缓存机制

    前言 缓存是一种提高数据读取性能的技术,在计算机中cpu和主内存之间读取数据存在差异,CPU和主内存之间有CPU缓存,而且在内存和硬盘有内存缓存.当主存容量远大于CPU缓存,或磁盘容量远大于主存时,哪 ...

  8. leetcode:146. LRU缓存机制

    题目描述: 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 ( ...

  9. LeetCode 146. LRU缓存机制(LRU Cache)

    题目描述 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (k ...

随机推荐

  1. gin 集成 consul

    "github.com/hashicorp/consul/api" package initialize import ( "fmt" "github ...

  2. 题解 「2017 山东一轮集训 Day7」逆序对

    题目传送门 Description 给定 $ n, k $,请求出长度为 $ n $ 的逆序对数恰好为 $ k $ 的排列的个数.答案对 $ 10 ^ 9 + 7 $ 取模. 对于一个长度为 $ n ...

  3. C++/CLR 使用(VS2012,VS2013,VS2015)编写

    转载自:http://www.th7.cn/system/win/201509/129417.shtml VS2010以及以前的版本,创建项目时都可以在CLR下找到"Windows窗体应用程 ...

  4. 来说说JPA、Hibernate、Spring Data JPA之间的什么关系?

    目录 JPA Hibernate Spring Data JPA 实践 来说说JPA.Hibernate.Spring Data JPA之间的什么关系 Java 持久层框架访问数据库的方式大致分为两种 ...

  5. python flask1

    以这个服务端代码为例,简单了解一下flask的运用. 1.app = Flask(__name__)记住就好了 2.@app.route("/")记住就好了:注意括号里的是调用这个 ...

  6. PyCharm中目录directory与包package的区别及相关import详解

    一.概念介绍 在介绍目录directory与包package的区别之前,先理解一个概念---模块 模块的定义:本质就是以.py结尾的python文件,模块的目的是为了其他程序进行引用. 目录(Dire ...

  7. Java:ConcurrentHashMap类小记-2(JDK7)

    Java:ConcurrentHashMap类小记-2(JDK7) 对 Java 中的 ConcurrentHashMap类,做一个微不足道的小小小小记,分三篇博客: Java:ConcurrentH ...

  8. OO--第三单元规格化设计 博客作业

    OO--第三单元规格化设计 博客作业 前言 第三单元,我们以JML为基础,先后完成了 PathContainer -> Graph -> RailwaySystem 这是一个递进的过程,代 ...

  9. Vue 报错Error in render: “TypeError: Cannot read properties of null (reading ‘xxx’)” found in

    前端vue报错 [Vue warn]: Error in render: "TypeError: Cannot read properties of null (reading 'name' ...

  10. Swift-技巧(一)缩放并填充图片

    摘要 直接操作图片来实现它的缩放或者填充多余空间,首选 UIGraphicsBeginImageContext 函数来实现,它就相当于一个画布,你甚至可以用它来涂鸦. 最近有一个需求,就是将图片先等比 ...