2021-01-23:LFU手擼,說下時間複雜度和空間複雜度。

福哥答案2021-01-23:
這道題複雜度過高,短期內很難寫出來。面試的時候不建議手撕代碼。
一個存節點的map+一個存桶的map+一個存桶的雙向鏈表。桶自己也是一個雙向鏈表。
存節點的map:key是鍵,value是節點。
存桶的map:key是次數,value是桶。
代碼用golang編寫,代碼以下:golang

package main

import (
    "container/list"
    "fmt"
)

func main() { 
    cache := Constructor(2)
    cache.Put(1, 1)
    cache.Put(2, 2)
    cache.Get(1)    // 返回 1
    cache.Put(3, 3) // 去除鍵 2
    cache.Get(2)    // 返回 -1(未找到)
    cache.Get(3)    // 返回 3
    cache.Put(4, 4) // 去除鍵 1
    cache.Get(1)    // 返回 -1(未找到)
    cache.Get(3)    // 返回 3
    cache.Get(4)    // 返回 4

}

type LFUCache struct { 
    Cap int
    Len int
    //map緩存,鍵存key,值存kv和先後
    KeyCache map[int]*list.Element //key存鍵,value存節點

    List *list.List

    FreqCache map[int]*list.Element //key存次數,value存桶
}

func Constructor(capacity int) LFUCache { 
    ret := LFUCache{ }
    ret.Cap = capacity
    ret.KeyCache = make(map[int]*list.Element)  //元素存節點
    ret.FreqCache = make(map[int]*list.Element) //元素存桶
    ret.List = list.New()
    return ret
}

func (this *LFUCache) Get(key int) int { 
    //已經找到當前元素了
    v := this.KeyCache[key]
    if v == nil { 
        fmt.Println(-1)
        return -1
    }
    //移動
    this.curNodeMoveToNextBucket(v)

    //返回當前元素的值
    fmt.Println(v.Value.([]int)[1])
    return v.Value.([]int)[1]
}

//當前節點移動到下一個桶
func (this *LFUCache) curNodeMoveToNextBucket(v *list.Element) { 
    //根據當前節點的次數找到當前桶
    curbucket := this.FreqCache[v.Value.([]int)[2]]

    //找下一桶,找不到建立新桶
    nextbucket := this.FreqCache[v.Value.([]int)[2]+1]
    if nextbucket == nil { 
        nextbucket = this.List.InsertAfter(list.New(), curbucket)
        this.FreqCache[v.Value.([]int)[2]+1] = nextbucket
    }

    //把當前節點放在下一桶裏
    //nextbucket.Value.(*list.List).PushBack(v.Value),這樣的代碼,leetcode不能經過。緣由是元素移動後,已經不是之前的元素了。因此map須要從新賦值。這個錯誤,我花了1個小時才找到,請謹慎。
    this.KeyCache[v.Value.([]int)[0]] = nextbucket.Value.(*list.List).PushBack(v.Value)

    //當前桶刪除當前節點
    curbucket.Value.(*list.List).Remove(v)

    //若是當前桶爲空,直接刪除當前桶。
    if curbucket.Value.(*list.List).Len() == 0 { 
        this.List.Remove(curbucket)
        delete(this.FreqCache, v.Value.([]int)[2])
    }

    //當前節點次數加1
    v.Value.([]int)[2]++
}

func (this *LFUCache) Put(key int, value int) { 
    if this.Cap == 0 { 
        return
    }
    if v, ok := this.KeyCache[key]; ok {  //緩存裏有
        //修改值
        v.Value.([]int)[1] = value

        //移動
        this.curNodeMoveToNextBucket(v)
    } else {  //緩存裏沒有
        if this.Len == this.Cap { 
            //獲取可能須要刪除的桶
            deleteBucket := this.List.Front()

            //獲取須要刪除的元素
            deleteE := deleteBucket.Value.(*list.List).Front()

            //刪除元素
            delete(this.KeyCache, deleteE.Value.([]int)[0])
            deleteBucket.Value.(*list.List).Remove(deleteE)

            //可能須要刪除的桶若是沒有元素,刪除桶。而且須要刪除的元素的次數不是1
            if deleteBucket.Value.(*list.List).Len() == 0 { 
                this.List.Remove(deleteBucket)
                delete(this.FreqCache, deleteE.Value.([]int)[2])
            }
        } else { 
            this.Len++
        }

        //獲取次數爲1的桶
        oneTimeBucket := this.FreqCache[1]

        //獲取不到就建立桶
        if oneTimeBucket == nil { 
            oneTimeBucket = this.List.PushFront(list.New())
            this.FreqCache[1] = oneTimeBucket
        }

        this.KeyCache[key] = oneTimeBucket.Value.(*list.List).PushBack([]int{ key, value, 1})

    }
}

執行結果以下:
在這裏插入圖片描述面試


評論緩存

相關文章
相關標籤/搜索