概述
????LRU 最近最少使用算法
- 设计LRU Cash 数据结构
- 设计方法
- 代码实现
- 总结
百度百科:
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
LeetCode:
146. LRU 缓存机制
✨ 大二学操作系统的时候学了LRU页面置换算法, 那时候只会用纸和笔手动模拟, 今天在看设计模式的策略模式的例子时看到了LRU evictionAlgo
, 索性去leetcode找到此题, 起初真的是没思路, 看了题解后, 发现太妙了, 用一个map 和 双向链表, map的使用可以使取值时间复杂度降低到O(1)
, 双向链表则可以模拟使用先后顺序
设计LRU Cash 数据结构
设计方法
put方法:
-
若存在key, 更新node的value值, 并将该node 移到链表头
-
若存在key, 先检查是否达到容量.
如果达到容量则删除链表尾元素.
最后构造节点, 将节点添加到链表头, 并加入cashMap
get方法:
- 不存在的key, 直接返回-1
- 存在则将该node 移到链表头
代码实现
type LRUCache struct {
capacity int
head, tail *DNode
cashMap map[int]*DNode
}
type DNode struct {
prev *DNode
next *DNode
key, value int
}
func Constructor(capacity int) LRUCache {
l := LRUCache{
capacity: capacity,
head: initNode(0, 0),
tail: initNode(0, 0),
cashMap: map[int]*DNode{},
}
l.head.next = l.tail
l.tail.prev = l.head
return l
}
func (l *LRUCache) Get(key int) int {
node, ok := l.cashMap[key]
if !ok {
return -1
}
l.removeToHead(node)
return node.value
}
func (l *LRUCache) Put(key int, value int) {
//存在
if node, ok := l.cashMap[key]; ok {
node.value = value
l.removeToHead(node)
return
}
//不存在
if len(l.cashMap) >= l.capacity {
removed := l.removeTail()
delete(l.cashMap, removed.key)
}
node := initNode(key, value)
l.cashMap[key] = node
l.addNodeToHead(node)
}
func initNode(key, value int) *DNode {
return &DNode{
key: key,
value: value,
}
}
func (l LRUCache) addNodeToHead(node *DNode) {
node.prev = l.head
node.next = l.head.next
l.head.next.prev = node
l.head.next = node
}
func (l LRUCache) removeTail() *DNode {
node := l.tail.prev
l.removeNode(node)
return node
}
func (l LRUCache) removeToHead(node *DNode) {
l.removeNode(node)
l.addNodeToHead(node)
}
func (l LRUCache) removeNode(node *DNode) {
node.prev.next = node.next
node.next.prev = node.prev
}
???? 一开始自己实现双向链表, 后来想到GO语言中有container/list, 以前也没用过, 这次写一下试试如何
import "container/list"
type LRUCache struct {
capacity int
dLinkedList *list.List
cash map[int]*list.Element
}
type DLinkNode struct {
key, value int
}
func (d DLinkNode) Value() interface{} {
return d.value
}
func Constructor(capacity int) LRUCache {
return LRUCache{
capacity: capacity,
dLinkedList: list.New(),
cash: map[int]*list.Element{},
}
}
func (l *LRUCache) Get(key int) int {
element, ok := l.cash[key]
if !ok {
return -1
}
l.dLinkedList.MoveToFront(element)
return element.Value.(*DLinkNode).value
}
func (l *LRUCache) Put(key int, value int) {
element, ok := l.cash[key]
//如果key已存在
if ok {
node := element.Value.(*DLinkNode)
node.value = value
l.dLinkedList.MoveToFront(element)
}
//如果key不存在
if len(l.cash) >= l.capacity {
back := l.dLinkedList.Back()
l.dLinkedList.Remove(back)
delete(l.cash, back.Value.(*DLinkNode).key)
}
node := &DLinkNode{
key: key,
value: value,
}
front := l.dLinkedList.PushFront(node)
l.cash[key] = front
}
总结
- 操作双向链表的时候要注意指向先后问题,比如: 写
addNodeToHead
的时候,l.head.next.prev = node
和l.head.next = node
的顺序一开始就搞错了, 找bug找了很长时间 - Go语言内置的数据结构,不够熟悉, 第二段代码实现中map的值类型是
*list.Element
元素句柄, 这样便于调用list
内置方法
最后
以上就是冷傲云朵为你收集整理的LRU 最近最少使用算法的全部内容,希望文章能够帮你解决LRU 最近最少使用算法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复