美文网首页
用go语言实现LFU缓存,两种方法实现_leetcode

用go语言实现LFU缓存,两种方法实现_leetcode

作者: fjxCode | 来源:发表于2018-11-09 11:48 被阅读0次

    LFU Cache用go语言实现LFU缓存,两种方法实现

    题目:

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.
    
    get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
    put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.
    
    Follow up:
    Could you do both operations in O(1) time complexity?
    
    Example:
    
    LFUCache cache = new LFUCache( 2 /* capacity */ );
    
    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1);       // returns 1
    cache.put(3, 3);    // evicts key 2
    cache.get(2);       // returns -1 (not found)
    cache.get(3);       // returns 3.
    cache.put(4, 4);    // evicts key 1.
    cache.get(1);       // returns -1 (not found)
    cache.get(3);       // returns 3
    cache.get(4);       // returns 4
    

    思路:

    • 双哈希实现,mapKvc存K-[V,Cnt]。mapFreq记录Cnt-[同频率节点]。min记录最小频率的一组,容量超限时,从这里清元素。
    • Get方法:更新mapKvs中的Cnt,mapFreq更新所在的频率组。mapFreq[min]组为空要处理下。
    • Put方法:存在key,则加入mapKvs,并用get()方法touch一次。不存在key,如果超容量先从mapFreq[min]清一个元素,加到mapKvs,mapFreq,更新min=1。
    • 细节:同步率的一组,可以用切片、链表。而用哈希效率更高些。代价是容量超限时,删除一个元素的代码长一些。

    错解:(未满足题意的一个要求)

    //仅能通过18/23个用例。
    
    
    type LfuVc struct {
        value int
        count int
    }
    
    type LFUCache struct {
        mapKvs  map[int]LfuVc
        mapFreq map[int]map[int]int
        min     int
        cap     int
    }
    
    func Constructor(capacity int) LFUCache {
        return LFUCache{
            mapKvs:  make(map[int]LfuVc, capacity),
            mapFreq: make(map[int]map[int]int, capacity),
            min:     0,
            cap:     capacity,
        }
    }
    
    func (this *LFUCache) Get(key int) int {
        lfuVcElem, ok := this.mapKvs[key]
        if !ok {
            return -1
        }
    
        this.mapKvs[key] = LfuVc{value: lfuVcElem.value, count: lfuVcElem.count + 1}
        cnt := lfuVcElem.count
        delete(this.mapFreq[cnt], key)
        _, ok = this.mapFreq[cnt+1]
        if !ok {
            this.mapFreq[cnt+1] = make(map[int]int, this.cap)
        }
        this.mapFreq[cnt+1][key] = 1
        //update min
        if cnt == this.min && len(this.mapFreq[cnt])==0{
            this.min++//由于被查的key的count由min升级到min+1,所以min+1必然非空
        }
    
        return lfuVcElem.value
    }
    
    func (this *LFUCache) Put(key int, value int) {
        if this.cap == 0 {
            return
        }
        _, ok := this.mapKvs[key]
        if ok {
            this.mapKvs[key] = LfuVc{value:value,count:this.mapKvs[key].count}//更新count留给this.Get()做
            this.Get(key)
            return
        }
        if len(this.mapKvs) >= this.cap {
            //if this.min>1{
            //  //满容,且都是高频元素,不执行Put
            //  return
            //}
            var minOneKey int
            for key,_ := range this.mapFreq[this.min]{
                minOneKey = key
                break
            }
            delete(this.mapKvs,minOneKey)
            delete(this.mapFreq[this.min],minOneKey)
        }
        this.mapKvs[key] = LfuVc{value: value, count: 1}
        if this.min != 1 {
            this.mapFreq[1] = make(map[int]int, this.cap)
        }
        this.mapFreq[1][key] = 1
        this.min = 1
    }
    

    问题分析:

    • 补充一个题目的要求,when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.
    • 所以虽然用Set存同频率KV的查找效率高,但无奈不符合题意,改用链表写。
    • 若是随机删除一个同频元素,就任选一个算法了。

    正确解:

    package main
    
    import (
        "container/list"
        "fmt"
    )
    
    func main() {
        c := Constructor(2)
        c.Put(2, 1)
        c.Put(1, 1)
        fmt.Println(c.Get(2))
        c.Put(4, 1)
        fmt.Println(c.Get(1))
        fmt.Println(c.Get(2))
    }
    
    type LfuVc struct {
        value int
        count int
    }
    
    type LFUCache struct {
        mapKvs  map[int]LfuVc
        mapFreq map[int]*list.List
        min     int
        cap     int
    }
    
    func Constructor(capacity int) LFUCache {
        return LFUCache{
            mapKvs:  make(map[int]LfuVc, capacity),
            mapFreq: make(map[int]*list.List, capacity),
            min:     0,
            cap:     capacity,
        }
    }
    
    func (this *LFUCache) Get(key int) int {
        lfuVcElem, ok := this.mapKvs[key]
        if !ok {
            return -1
        }
    
        this.mapKvs[key] = LfuVc{value: lfuVcElem.value, count: lfuVcElem.count + 1}
        cnt := lfuVcElem.count
    
        for e := this.mapFreq[cnt].Front();e!=nil;e = e.Next(){
            if e.Value.(int) == key{
                this.mapFreq[cnt].Remove(e)
                break
            }
        }
        if this.mapFreq[cnt+1] == nil{
            this.mapFreq[cnt+1] = list.New()
        }
        this.mapFreq[cnt+1].PushBack(key)
        //update min
        if cnt == this.min && this.mapFreq[cnt].Len()==0{
            this.min++//由于被查的key的count由min升级到min+1,所以min+1必然非空
        }
    
        return lfuVcElem.value
    }
    
    func (this *LFUCache) Put(key int, value int) {
        if this.cap == 0 {
            return
        }
        _, ok := this.mapKvs[key]
        if ok {
            this.mapKvs[key] = LfuVc{value:value,count:this.mapKvs[key].count}//更新count留给this.Get()做
            this.Get(key)
            return
        }
        if len(this.mapKvs) >= this.cap {
            //if this.min > 1 {
            //  //满容,且都是高频元素,不执行Put
            //  return
            //}
            l := this.mapFreq[this.min]
            delete(this.mapKvs, l.Front().Value.(int))
            l.Remove(l.Front())
        }
        this.mapKvs[key] = LfuVc{value: value, count: 1}
        if this.min != 1 {
            this.mapFreq[1] = list.New()
        }
        this.mapFreq[1].PushBack(key)
        this.min = 1
    }
    

    相关文章

      网友评论

          本文标题:用go语言实现LFU缓存,两种方法实现_leetcode

          本文链接:https://www.haomeiwen.com/subject/mknnxqtx.html