LRU-K
LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。也就是说没有到达K次访问的数据并不会被缓存,这也意味着需要对于缓存数据的访问次数进行计数,并且访问记录不能无限记录,也需要使用替换算法进行替换。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
缺点
缺点是需要一块空间存储history
之前我们学的LRU算法其实就是LRU-1, 一般会考虑才用LRU-2
核心思想
有两个存储空间,一块叫历史空间,存储访问次数低于K的缓存;另一块叫缓存空间,存储超过K次的数据。历史空间按照一定的算法淘汰过期数据(FIFO, LRU,文章中采用的是LRU),当数据访问次数达到K次,则将其加入到缓存空间中,并从历史空间中删除。
代码
package main
import (
"container/list"
"fmt"
)
type Item struct {
k int
v int
visit int
}
func NewItem(k int, v int, visit int) *Item {
return &Item{k: k, v: v, visit: visit}
}
type LRUKCache struct {
k int
historyCap int
history *list.List
historyData map[int]*list.Element
cacheCap int
cache *list.List
cacheData map[int]*list.Element
}
func NewLRUKCache(k int, historyCap int, cacheCap int) LRUKCache {
return LRUKCache{
k: k,
historyCap: historyCap,
history: list.New(),
historyData: make(map[int]*list.Element),
cacheCap: historyCap,
cache: list.New(),
cacheData: make(map[int]*list.Element),
}
}
func (ll *LRUKCache) Get(k int) int {
item, ok := ll.cacheData[k]
if ok {
ll.cache.MoveToFront(item)
return item.Value.(*Item).v
}
item, ok = ll.historyData[k]
if ok {
node := item.Value.(*Item)
node.visit++
if node.visit == ll.k {
ll.addCache(item)
}
return node.v
}
return -1
}
func (ll *LRUKCache) Put(k int, v int) {
if ll.cacheCap == 0 {
return
}
item, ok := ll.cacheData[k]
if ok {
item.Value.(*Item).v = v
ll.cache.MoveToFront(item)
return
}
item, ok = ll.historyData[k]
if ok {
node := item.Value.(*Item)
node.v = v
node.visit++
if node.visit == k {
ll.addCache(item)
}
return
}
node := NewItem(k, v, 1)
ll.addHistory(k, node)
}
func (ll *LRUKCache) addHistory(k int, item interface{}) {
if ll.history.Len() == ll.historyCap {
oldItem := ll.history.Back()
ll.history.Remove(oldItem)
delete(ll.historyData, oldItem.Value.(*Item).k)
}
ele := ll.history.PushFront(item)
ll.historyData[k] = ele
}
func (ll *LRUKCache) addCache(item *list.Element) {
node := item.Value.(*Item)
if ll.cache.Len() == ll.cacheCap {
oldItem := ll.cache.Back()
ll.cache.Remove(oldItem)
delete(ll.cacheData, oldItem.Value.(*Item).k)
}
ele := ll.cache.PushFront(node)
ll.cacheData[node.k] = ele
ll.history.Remove(item)
delete(ll.historyData, node.k)
}