golang实现lru缓存

简介: golang实现lru缓存

公众号merlinsea


  • 请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1,golang可以返回多关键字,用于标识是否存在
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字
  • 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行
  • 解题思路
  • 首先要求get(key)需要在O(1)的时间复杂度下获取结果,因此这里可以使用map来存储数据,map的key是一个int类型,map的value是一个地址,这个地址指向真正的节点。
  • 另外由于put操作中在数据量超过capacity的时候需要把最久没有使用到的关键字删除,因此可以知道还需要使用双向链表来记录数据。
  • 当put操作的时候发现lru缓存的容量已经满了的情况,需要将tail 的前驱节点(即最久没有访问过的节点)删除。 这里的双向链表采用了虚拟头节点和尾节点的编程技巧。
  • 为什么使用双向链表
  • 双向链表每个节点都有前驱和后继节点,可以通过链表的顺序关系可以来判断哪些节点是最近使用的,哪些节点是最早使用的。
  • 双向链表中删除某个节点可以保证O(1)的时间复杂度,首先在map中通过key快速定位下节点信息,得到节点信息以后可以把这个节点通过调整指针指向来使其移动到链表中的第一位【表示最近访问过了】。


640.png

  • 代码实现


package lru_cache
// 定义双向链表的节点
type Node struct {
  Key  int
  Val  int
  Pre  *Node
  Next *Node
}
// lru缓存
type LRUCache struct {
  KeyNodeMap map[int]*Node
  Head       *Node
  Tail       *Node
  Capacity   int
}
// 构造器,通过new()可以创建一个指向结构体的指针
func Constructor(capacity int) LRUCache {
  lruCathe := LRUCache{
    KeyNodeMap: make(map[int]*Node),
    Head:       new(Node),
    Tail:       new(Node),
    Capacity:   capacity,
  }
  lruCathe.Head.Next = lruCathe.Tail
  lruCathe.Tail.Pre = lruCathe.Head
  return lruCathe
}
func (this *LRUCache) Get(key int) int {
  nodePtr, ok := this.KeyNodeMap[key]
  if !ok {
    return -1
  }
  // 将结果放到第一个
  this.moveFirst(nodePtr)
  return nodePtr.Val
}
func (this *LRUCache) Put(key int, value int) {
  // 判断是否存在
  nodePtr, ok := this.KeyNodeMap[key]
  if ok {
    // 存在则进行替换
    nodePtr.Val = value
    // 将节点移到最前面
    this.moveFirst(nodePtr)
  } else {
    if this.Capacity == len(this.KeyNodeMap) {
      // 容量已经满了,先删除tail(包括双向链表和map)
      oldNodePtr := this.Tail.Pre
      this.Tail.Pre = oldNodePtr.Pre
      oldNodePtr.Pre.Next = this.Tail
      delete(this.KeyNodeMap, oldNodePtr.Key)
    }
    // 插入
    nodePtr := &Node{
      Key:  key,
      Val:  value,
      Pre:  this.Head,
      Next: this.Head.Next,
    }
    this.Head.Next.Pre = nodePtr
    this.Head.Next = nodePtr
    this.KeyNodeMap[key] = nodePtr
  }
}
 // 将节点nodePtr移动的第一位表示最近访问过的节点
func (this *LRUCache) moveFirst(nodePtr *Node) {
  nodePtr.Pre.Next = nodePtr.Next
  nodePtr.Next.Pre = nodePtr.Pre
  nodePtr.Next = this.Head.Next
  this.Head.Next.Pre = nodePtr
  this.Head.Next = nodePtr
  nodePtr.Pre = this.Head
}
func main() {
  lruCache := lru_cache.Constructor(2)
  lruCache.Put(1, 1)
  lruCache.Put(2, 2)
  v := lruCache.Get(1)
  fmt.Println(v)
  lruCache.Put(3, 3)
  v = lruCache.Get(2)
  fmt.Println(v)
  lruCache.Put(4, 4)
  v = lruCache.Get(1)
  fmt.Println(v)
  v = lruCache.Get(3)
  fmt.Println(v)
  v = lruCache.Get(4)
  fmt.Println(v)
}

golang版本永久算法题训练营~~

奔跑的小梁,公众号:梁霖编程工具库

算法训练营golang专题刷题来啦!!!!

相关文章
|
2月前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
62 3
|
2月前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
80 2
|
4月前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
214 1
|
5月前
|
缓存 Python
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
|
4月前
|
存储 缓存 Java
|
4月前
|
存储 缓存 算法
Python 从零开始实现一个简单的LRU缓存
Python 从零开始实现一个简单的LRU缓存
45 0
|
5月前
|
缓存 算法 前端开发
前端 JS 经典:LRU 缓存算法
前端 JS 经典:LRU 缓存算法
99 0
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
78 6
|
1月前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
1月前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构