写点什么

[Day23]-[数据结构] 手写 LRU

作者:方勇(gopher)
  • 2022 年 4 月 23 日
  • 本文字数:1502 字

    阅读完需:约 5 分钟

146. LRU 缓存

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存

  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

 

题解:

LRU 淘汰策略:

1、如果达到最大容量最近未使用将被淘汰。

2、采用 map + 双向链表实现

3、每次访问不存在的 key 返回-1

4、每次访问存在的 key,则将 key 对应的元素节点移动到链表的尾部(也可以是头部,这里采用尾部)

5、每次 put (key,value),如果 key 存在,删除就的 map 中 dkey,并将新能的 key,value 放到链路的尾部

6、如果 put 的时候 key 不存在,则要判断 capacity 是否到达了上限,如果未到达上限,则将 key,value 添加到链表尾部,如果达到了上限则 删除链表的头部的元素,再将 key,value 添加到链表的尾部

7、map 存的是 key 对应的 Node 节点指针


下面是具体的实现:

// 用于存储数据type Node struct {	key  int	val  int	prev *Node	next *Node}
// 双向链表type DoubleListNode struct { head *Node tail *Node size int}
func (d *DoubleListNode) add(node *Node) { node.next = d.tail node.prev = d.tail.prev d.tail.prev.next = node d.tail.prev = node d.size++}
func (d *DoubleListNode) remove(node *Node) { node.prev.next = node.next node.next.prev = node.prev d.size--}
func (d *DoubleListNode) removeFirst() *Node { first := d.head.next if first == d.tail { return nil } d.remove(first) return first}
func (d *DoubleListNode) Size() int { return d.size}
type LRUCache struct { hashMap map[int]*Node cache *DoubleListNode cap int}
func Constructor(capacity int) LRUCache { head, tail := &Node{}, &Node{} head.next = tail tail.prev = head return LRUCache{ cap: capacity, hashMap: make(map[int]*Node), cache: &DoubleListNode{ head: head, tail: tail, size: 0, }, }}
func (l *LRUCache) Get(key int) int { if node, ok := l.hashMap[key]; ok { l.makeRecently(key) // 将元素添加到哦队列尾部 return node.val }
return -1}
func (l *LRUCache) Put(key int, value int) { if _, ok := l.hashMap[key]; ok { l.deleteKey(key) // key 对应的val 可能有变化,得把老的删了,1=>2 变成了1=>3 l.addRecently(key, value) return } if l.cap == l.cache.Size() { l.removeLeastRecently() } l.addRecently(key, value)}
// 将某个key提升为最近使用func (l *LRUCache) makeRecently(key int) { node := l.hashMap[key] l.cache.remove(node) l.cache.add(node)}
// 添加一个keyfunc (l *LRUCache) addRecently(key, val int) { node := &Node{ key: key, val: val, } l.hashMap[key] = node l.cache.add(node)}
// 删除一个keyfunc (l *LRUCache) deleteKey(key int) { node := l.hashMap[key] l.cache.remove(node) delete(l.hashMap, key)}
// 删除最久未使用keyfunc (l *LRUCache) removeLeastRecently() { node := l.cache.removeFirst() if node != nil { delete(l.hashMap, node.key) }}
复制代码


用户头像

Dead or Alive. 生存战斗是知识的源泉! 2018.11.08 加入

我是一名SRE哨兵,目前是好大夫基础架构部高级工程师。专注于 SRE,微服务、中间件的稳定性和可用性建设,整体负责好大夫服务治理云平台的设计和搭建!

评论

发布
暂无评论
[Day23]-[数据结构]手写LRU_LeetCode_方勇(gopher)_InfoQ写作社区