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

思路:哈希表 + 双向链表

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

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

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

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

代码

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

type LRUCache struct {  //LRU 缓存结构
capacity int // 容量
m map[int]*Node //哈希表
cache *NodeList //双向链表
} type Node struct{ //节点结构
key int
value int
prev *Node //前一个节点
next *Node //后一个节点
} func initNode(key,value int)*Node{ //初始化节点
return &Node{
key:key,
value:value,
}
} type NodeList struct{ //链表结构
head *Node //链表头节点
last *Node //链表尾节点
size int //元素个数
} func initList ()*NodeList{ //初始化链表
dil:=&NodeList{
head:initNode(0,0),
last:initNode(0,0),
size:0,
}
dil.head.next=dil.last
dil.last.prev=dil.head return dil
} func (this *NodeList)addNodeinlist(node *Node){ //向链表中插入节点,向链表尾部插节点
node.prev=this.last.prev
this.last.prev=node
node.prev.next=node
node.next=this.last this.size++
} func (this *NodeList)deleteNodeinlist (node *Node){ //删除链表中的某一结点
node.prev.next=node.next
node.next.prev=node.prev
node.next=nil
node.prev=nil
this.size--
} func (this *NodeList)delfirstNode()*Node{ //删除第一个节点,并且返回
if this.head.next==this.last{
return nil
}
t:=this.head.next
this.deleteNodeinlist(t) return t
} func Constructor(capacity int) LRUCache { //初始化 LRU 缓存
return LRUCache{
capacity:capacity,
m:make(map[int]*Node,0),
cache:initList(),
}
} func (this *LRUCache)addkey(key,value int){ //添加元素
node:=initNode(key,value)
//增加map映射
this.m[key]=node //在链表中添加元素
this.cache.addNodeinlist(node)
} func (this *LRUCache)makekey(key int){ // 将某个 key 调整为最近使用的元素
//找到节点
node:=this.m[key]
//删除节点
this.cache.deleteNodeinlist(node)
// 添加到链表尾部
this.cache.addNodeinlist(node)
} func (this *LRUCache)deletekey(key int){ //删除元素 //删除链表中节点
this.cache.deleteNodeinlist(this.m[key])
//删除map映射
delete(this.m,key)
} func (this *LRUCache)deletefirkey(){ //删除最久未使用的元素
// 链表的第一个就是最近最少使用的元素
node:=this.cache.delfirstNode() // 删除映射
delete(this.m,node.key)
} func (this *LRUCache) Get(key int) int {
if _,ok:=this.m[key];ok{
//存在
this.makekey(key) //将某个 key 调整为最近使用的元素
return this.m[key].value
}else{
//不存在
return -1
} } func (this *LRUCache) Put(key int, value int) {
// 检查key存不存在
if _,ok:=this.m[key];ok{
//存在
//删除元素
this.deletekey(key) //添加元素到尾部
this.addkey(key,value) }else{
//不存在
if this.capacity==this.cache.size{
//缓存达到上限
//删除最久未使用的元素
this.deletefirkey()
}
//添加元素到尾部
this.addkey(key,value)
}
}

参考:

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. 自定义view---仪表盘--kotlin

    我们知道一个自定义view一般来说需要继承view或者viewGroup并实现onMeasure, onLayout, onDraw方法. 其中onMeasure用于测量计算该控件的宽高, onLay ...

  2. spring boot log4j2 最佳实践

    为什么选择 log4j2 Log4j2 使用了 LMAX Disruptor 库.在多线程场景中,异步 Logger 的吞吐量比 Log4j 1.x 和 Logback 高 18 倍,延迟低几个数量级 ...

  3. 从 MVC 到使用 ASP.NET Core 6.0 的最小 API

    从 MVC 到使用 ASP.NET Core 6.0 的最小 API https://benfoster.io/blog/mvc-to-minimal-apis-aspnet-6/ 2007 年,随着 ...

  4. Java和jmeter环境变量的配置来这就对了!

    java环境变量设置 1.新建JAVA_HOME 变量 点击 新建按钮 变量名:JAVA_HOME 变量值:电脑上JDK安装的绝对路径 2.新建/修改 CLASSPATH 变量 如果存在 CLASSP ...

  5. clock时钟

    ①时钟的偏移(skew):时钟分支信号在到达寄存器的时钟端口过程中,都存在有线网等延时,由于延时,到达寄存器时钟端口的时钟信号存在有相位差,也就是不能保证每一个沿都对齐,这种差异称为时钟偏移(cloc ...

  6. 欧姆龙plc通讯协议格式

    欧姆龙CPM1A型plc与上位计算机通信的顺序是上位机先发出命令信息给PLC,PLC返回响应信息给上位 机.每次通信发送/接受的一组数据称为一"帧".帧由少于131个字符的数据构成 ...

  7. Beta发布声明

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 Beta-发布声明 我们是谁 删库跑路对不队 我们在做什么 题士 进度如何 进度总览 一.功能与特性 1.一 ...

  8. logstash的mutate过滤器的使用

    logstash的mutate过滤器的使用 一.背景 二.需求 三.实现步骤 1.安装 `csv codec` 插件 2.准备需要读取的文件数据 3.编写 pipeline ,读取和输出数据 4.mu ...

  9. Noip模拟35 2021.8.10

    考试题目变成四道了,貌似确实根本改不完... 不过给了两个小时颓废时间确实很爽(芜湖--) 但是前几天三道题改着不是很费劲的时候为什么不给放松时间, 非要在改不完题的时候颓?? 算了算了不碎碎念了.. ...

  10. 8.18考试总结[NOIP模拟43]

    又挂了$80$ 好气哦,但要保持优雅.(草 T1 地衣体 小小的贪心:每次肯定从深度较小的点向深度较大的点转移更优. 模拟一下,把边按链接点的子树最大深度排序,发现实际上只有上一个遍历到的点是对当前考 ...