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

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)