golang实现LFU缓存算法

简介: golang实现LFU缓存算法

公众号merlinsea


  • 相关内容导航

LRU最近最少使用算法

奔跑的小梁,公众号:梁霖编程工具库golang实现lru缓存


  • 题目描述
  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
  • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1
  • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键
  • 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

  • 示例

640.png

  • 解题思路
  • 参考之前求解LRU的算法思路,改动点是当访问了某个key的时候,这个key需要移动到一个“合适”的位置,这里的合适指的是要按照每个key的访问频次来排序,当频次相同的访问时间越近的key优先级越高。

640.png


  • 代码实现


type Node struct{
    Val int
    Key int
    // 记录每个节点key出现的次数
    Cnt int
    Pre *Node
    Next *Node
}
type LFUCache struct {
    KeyValueMap map[int]*Node
    Head *Node
    Tail *Node
    Capacity int
}
func Constructor(capacity int) LFUCache {
    lfuCache := LFUCache{
        KeyValueMap : make(map[int]*Node),
        Head : new(Node),
        Tail : new(Node),
        Capacity: capacity,
    }
    lfuCache.Head.Next = lfuCache.Tail
    lfuCache.Tail.Pre = lfuCache.Head
    return lfuCache
}
func (this *LFUCache) Get(key int) int {
    nodePtr,ok := this.KeyValueMap[key]
    if !ok {
        return -1
    }
    nodePtr.Cnt++
    // 将nodePtr调整到合适的位置
    this.move(nodePtr)
    return nodePtr.Val
}
func (this *LFUCache) Put(key int, value int)  {
    nodePtr,ok := this.KeyValueMap[key]
    if ok {
        // 说明已经存在了
        nodePtr.Val = value
        nodePtr.Cnt++
        this.move(nodePtr)
    }else{
        if len(this.KeyValueMap) == this.Capacity {
            //定位下待删除的节点
            p := this.Tail.Pre
            // 执行删除
            this.Tail.Pre = p.Pre
            p.Pre.Next = this.Tail
            delete(this.KeyValueMap,p.Key)
        }
        // 构造节点
        newNodePtr := &Node{
            Key : key,
            Val : value,
            Cnt : 1,
            Pre : this.Tail.Pre,
            Next : this.Tail,
        }
        this.Tail.Pre.Next = newNodePtr
        this.Tail.Pre = newNodePtr
        this.KeyValueMap[key] = newNodePtr
        // 移动
        this.move(newNodePtr)
    }
}
// 考虑如何快速定位节点需要移动到的位置
func (this *LFUCache) move(nodePtr *Node){
    p := nodePtr.Pre
    nodePtr.Pre.Next = nodePtr.Next
    nodePtr.Next.Pre = nodePtr.Pre
    // 移动,这里假定越靠近head节点标识这个节点是最近访问过的,因此这里需要在相等的时候p继续向head移动
    for p!=this.Head && nodePtr.Cnt >= p.Cnt {
        p = p.Pre
    }
    // 将nodePtr插入到p的next
    nodePtr.Next = p.Next
    p.Next.Pre = nodePtr
    nodePtr.Pre = p
    p.Next = nodePtr
}


  • 代码优化思路
  • 可以将双向链表做成优先队列,通过优先队列可以保证每次删除的时间复杂度是logn级别,在本次的代码中,在极端的情况下,比如LFU的容量做的无限大的时候,不断进行put操作,会导致move方法的时间复杂度是On


golang版本永久算法题训练营本年最低价~

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

golang算法训练营价格调整通知23年7月8日【本年最低价,后期涨价】

相关文章
|
3月前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
72 3
|
2月前
|
存储 缓存 算法
分布式缓存有哪些常用的数据分片算法?
【10月更文挑战第25天】在实际应用中,需要根据具体的业务需求、数据特征以及系统的可扩展性要求等因素综合考虑,选择合适的数据分片算法,以实现分布式缓存的高效运行和数据的合理分布。
|
3月前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
84 2
|
5月前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
234 1
|
6月前
|
存储 算法 Java
高并发架构设计三大利器:缓存、限流和降级问题之滑动日志算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之滑动日志算法问题如何解决
|
6月前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
|
4月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
147 4
Golang语言之管道channel快速入门篇
|
4月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
73 4
Golang语言文件操作快速入门篇
|
4月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
120 3
Golang语言之gRPC程序设计示例
|
4月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
101 4