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
}
相关推荐
京东零售技术7 小时前
告别 “盲买”!京东 AI 试穿 Oxygen Tryon:让服饰购物从“想象”到“所见即所得”
算法
小白菜又菜7 小时前
Leetcode 2273. Find Resultant Array After Removing Anagrams
算法·leetcode·职场和发展
milanyangbo7 小时前
谁生?谁死?从引用计数到可达性分析,洞悉GC的决策逻辑
java·服务器·开发语言·jvm·后端·算法·架构
Swift社区7 小时前
LeetCode 409 - 最长回文串 | Swift 实战题解
算法·leetcode·swift
Lester_11018 小时前
嵌入式学习笔记 - 用泰勒公式解决 tanh函数
笔记·学习·算法
cr7xin8 小时前
缓存查询逻辑及问题解决
数据库·redis·后端·缓存·go
学习编程之路8 小时前
Rust内存对齐与缓存友好设计深度解析
开发语言·缓存·rust
JMzz8 小时前
Rust 中的内存对齐与缓存友好设计:性能优化的隐秘战场 ⚡
java·后端·spring·缓存·性能优化·rust