LRU-K golang 魔改leetcode-LRU题目 LRU

微派三面的时候,面试官问了LRU-K,当时没实现出来,受益良多,事后去魔改了下LeetCode146-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) 的平均时间复杂度运行。

 

比起这道题,我多设计了一个History队列~~

敲出来简单实现了下,以后有机会或者认识更加深入之后再重新总结写一篇文章,以下附一下我的思路:

其实history队列相当于LRU_Cache,只是需要维护一个K值。于是我多附加了一个map[int]int,用于记录对应Key被调用了多少次!当时面试官提供了结构体维护的思路,其实都可以,可能map会耗费多点性能?(存疑)

LRU其实就是LRU-1!!!

 

相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。详细实现如下

 

(1). 数据第一次被访问,加入到访问历史列表;

(2). 如果数据在访问历史列表里后没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;

(3). 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;

(4). 缓存数据队列中被再次访问后,重新排序;

(5). 需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。

LRU-K具有LRU的优点,同时能够避免LRU的缺点,实际应用中LRU-2是综合各种因素后最优的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史访问记录清除掉。

 

package main
​
import "fmt"
​
type LRUCache struct {
    size int
    capacity int
    cache map[int]*DLinkNode
    Head, Tail *DLinkNode
    his *History_cache
}
​
type History_cache struct {
    k int
    size int
    capacity int
    his_cache map[int]*DLinkNode
    Head, Tail *DLinkNode
    exist_nums map[int]int
}
​
type DLinkNode struct {
    key,value int
    Pre, Next *DLinkNode
}
​
func InitDlinkNode(key, value int) *DLinkNode {
    return &DLinkNode{key,value,nil,nil}
}
​
func Constructor_LRU(capacity int, h *History_cache) LRUCache {
    l := LRUCache{
        0,
        capacity,
        map[int]*DLinkNode{},
        InitDlinkNode(0, 0),
        InitDlinkNode(0, 0),
        h,
    }
    l.Head.Next = l.Tail
    l.Tail.Pre = l.Head
    return l
}
​
func Constructor_His(capacity, k int) History_cache {
    h := History_cache{
        k,
        0,
        capacity,
        map[int]*DLinkNode{},
        InitDlinkNode(0, 0),
        InitDlinkNode(0, 0),
        map[int]int{},
    }
    h.Head.Next = h.Tail
    h.Tail.Pre = h.Head
    return h
}
​
​
func (lru *LRUCache) Get(key int) int {
    if _,ok := lru.cache[key];!ok {
        return -1
    }
    node := lru.cache[key]
    lru.UpdateToHead(node, lru.Head)
    return node.value
}
​
​
func (lru *LRUCache) Put(key int, value int)  {
    //判断lru缓存中有无key
    if _,ok := lru.cache[key];!ok {
        //如果无key,判断在历史队列中有无
        if _, ok2 := lru.his.his_cache[key];!ok2{
            //如果无,则新增
            node := InitDlinkNode(key, value)
            lru.his.exist_nums[key]++
            if lru.his.size >= lru.his.capacity {
                lru.DeleteLast(&lru.his.size, lru.his.his_cache, lru.his.Tail)
            }
            lru.his.his_cache[key] = node
            lru.InsertNewHead(&lru.his.size, node, lru.his.Head)
        }else{
            //如果有,则分两种情况 1.<k 2.出历史队列,进入lru缓存
            lru.his.exist_nums[key]++
            if lru.his.exist_nums[key]>=lru.his.k{
                node := lru.his.Head
                for node.Next!=lru.his.his_cache[key]{
                    node = node.Next
                }
                node.Next = node.Next.Next
                lru.his.size--
                delete(lru.his.his_cache, key)
                delete(lru.his.exist_nums, key)
                if lru.size>=lru.capacity{
                    lru.DeleteLast(&lru.size, lru.cache, lru.Tail)
                }
                new_node := InitDlinkNode(key, value)
                lru.cache[key] = new_node
                lru.InsertNewHead(&lru.size, new_node, lru.Head)
            }else{
                node := lru.his.his_cache[key]
                node.value = value
                lru.UpdateToHead(node, lru.his.Head)
            }
        }
    }else {
        node := lru.cache[key]
        node.value = value
        lru.UpdateToHead(node, lru.Head)
    }
}
​
func (lru *LRUCache) UpdateToHead(node *DLinkNode, linkHead *DLinkNode) {
    node.Pre.Next = node.Next
    node.Next.Pre = node.Pre
    temp := linkHead.Next
    linkHead.Next = node
    node.Pre = linkHead
    node.Next = temp
    temp.Pre = node
 
}
​
func (lru *LRUCache) DeleteLast(size *int, c map[int]*DLinkNode, linkTial *DLinkNode) {
    node := linkTial.Pre
    linkTial.Pre = node.Pre
    node.Pre.Next = node.Next
    node.Pre = nil
    node.Next = nil
    (*size)--
    delete(c, node.key)
}
​
func (lru *LRUCache) InsertNewHead(size *int, node, linkHead *DLinkNode) {
    temp := linkHead.Next
    linkHead.Next = node
    node.Pre = linkHead
    temp.Pre = node
    node.Next = temp
    (*size)++
}
​
​
​
func main() {
    his := Constructor_His(2, 2)
    lru := Constructor_LRU(2, &his)
    fmt.Println("----未输入数据前----")
    fmt.Println("历史队列的长度:", lru.his.size)
    fmt.Println("LRU缓存的长度:", lru.size)
    fmt.Println()
    fmt.Println("----第一次PUT(1, 2)----")
    lru.Put(1, 2)
    fmt.Println("历史队列的长度:", lru.his.size)
    fmt.Println("LRU缓存的长度:", lru.size)
    fmt.Println("第一次GET:", lru.Get(1))
    fmt.Println("历史队列对应值出现次数:", lru.his.exist_nums[1])
    fmt.Println()
    fmt.Println("----第二次次PUT(1, 2)----")
    lru.Put(1, 2)
    fmt.Println("历史队列的长度:", lru.his.size)
    fmt.Println("LRU缓存的长度:", lru.size)
    fmt.Println("第二次GET:", lru.Get(1))
    fmt.Println("历史队列对应值出现次数:", lru.his.exist_nums[1])
​
}

这台电脑windows下没装环境,所以用白板敲了之后,在菜鸟go在线工具下编译的~~~~

 

posted @ 2022-04-04 22:50  peterrrr  阅读(329)  评论(1编辑  收藏  举报