LeetCode LRU缓存

题目地址:https://leetcode.cn/problems/lru-cache/description/?envType=study-plan-v2\&envId=top-100-liked

请你设计并实现一个满足 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) 的平均时间复杂度运行。

go 复制代码
// 把 哈希表(map) 和 双向链表(doubly linked list) 结合:
// map 用于用键快速定位缓存节点(O(1)查找)。
// 双向链表维护使用顺序,链表头表示最近使用(MRU),链表尾表示最久未使用(LRU)。当容量超出时,从尾部删除节点并同时从 map 中移除。

type LRUCache struct {
	size     int
	capacity int
	cache    map[int]*DLinkedNode
	head     *DLinkedNode
	tail     *DLinkedNode
}

type DLinkedNode struct {
	key, val   int
	prev, next *DLinkedNode
}

func initDLinkedNode(key, val int) *DLinkedNode {
	return &DLinkedNode{key: key, val: val}
}

func Constructor(capacity int) LRUCache {
	lru := &LRUCache{
		size:     0,
		capacity: capacity,
		cache:    make(map[int]*DLinkedNode, capacity),
		head:     new(DLinkedNode),
		tail:     new(DLinkedNode),
	}
	lru.head.next = lru.tail
	lru.tail.prev = lru.head
	return *lru
}

// 在 map 中查找节点:
// 未命中:返回 -1(或指定的"未找到"值)
// 命中:返回值前,必须把该节点 移动到链表头部(标记为最近使用)
// 移动步骤(moveToHead):removeNode(node) ------ 将 node 从当前位置断开(调整前后节点指针)addToHead(node) ------ 将 node 插入到 head 后面
func (this *LRUCache) Get(key int) int {
	node := this.cache[key]
	if node != nil {
		this.moveToHead(node)
		return node.val
	}
	return -1
}

// 在 map 中查找是否已有该 key:若存在:更新 node.value;然后 moveToHead(node)
// 若不存在:构造新节点 node(key,value) addToHead(node) map[key] = node

// 如果 size == capacity,
// 则需要 evict:removed := removeTail() (取得 tail.prev)delete(map, removed.key)
// removeTail():取 tail.prev(真实尾节点),执行 removeNode(removed) 并返回 removed
func (this *LRUCache) Put(key int, value int) {
	node := this.cache[key]
	if node != nil {
		node.val = value
		this.cache[key] = node
		this.moveToHead(node)
		return
	}
	node = &DLinkedNode{key: key, val: value}
	if this.size == this.capacity {
		removed := this.removeTail()
		delete(this.cache, removed.key)
		this.size--
	}
	this.addToHead(node)
	this.size++
	this.cache[key] = node

}

// 对 双向链表的操作
func (lru *LRUCache) addToHead(node *DLinkedNode) {
	node.next = lru.head.next
	lru.head.next.prev = node
	lru.head.next = node
	node.prev = lru.head
}

func (lru *LRUCache) removeNode(node *DLinkedNode) {
	preNode := node.prev
	preNode.next = node.next
	node.next.prev = preNode
}

func (lru *LRUCache) moveToHead(node *DLinkedNode) {
	lru.removeNode(node)
	lru.addToHead(node)
}

func (lru *LRUCache) removeTail() *DLinkedNode {
	tail := lru.tail.prev
	lru.removeNode(tail)
	return tail
}
相关推荐
旺仔仔仔10 小时前
# 机器人项目实战(1):选相机、试深度,再到手眼标定
算法
CoderYanger10 小时前
A.每日一题:1614. 括号的最大嵌套深度
java·程序人生·算法·leetcode·面试·学习方法
yuki蜜语10 小时前
别只会调包跑模型!一文讲透大模型训练底层的“递推、递归、三明治”三重逻辑
算法
JWASX10 小时前
Java 转 go 学习 - GRPC(1)
学习·golang
北冥you鱼10 小时前
Go 语言 Channel 机制详解:从原理到实战
开发语言·后端·golang
谢亮_vipxieliang11 小时前
Go 并发控制进阶:sync 包高级原语与 Context 生命周期管理
开发语言·后端·golang
成旭先生11 小时前
银行卡识别 API:一张照片读出卡号、BIN 与发卡行
人工智能·算法·ocr·api接口·金融科技·银行卡识别·支付风控
陌上花开缓缓归以11 小时前
mebdlts3.4.1安全启动全流程
服务器·算法
Nebula_g11 小时前
JavaSE拓展:可变参数
java·开发语言·算法·安全·javase·可变参数
129Lab11 小时前
BMS 核心算法:SOC 估算从安时积分到 LSTM-UKF 串级融合的 30 年演进(附 Python 实现)
python·算法·lstm·python3.11·bms·soc估算