LFU快取策略

2021-08-07 14:22:00 字數 3042 閱讀 8255

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

這道題是讓我們實現最近不常用頁面置換演算法lfu (least frequently used), 之前我們做過一道類似的題lru cache,讓我們求最近最少使用頁面置換演算法lru (least recnetly used)。兩種演算法雖然名字看起來很相似,但是其實是不同的。顧名思義,lru演算法是首先淘汰最長時間未被使用的頁面,而lfu是先淘汰一定時間內被訪問次數最少的頁面。光說無憑,舉個例子來看看,比如說我們的cache的大小為3,然後我們按順序存入 5,4,5,4,5,7,這時候cache剛好被裝滿了,因為put進去之前存在的數不會占用額外地方。那麼此時我們想再put進去乙個8,如果使用lru演算法,應該將4刪除,因為4最久未被使用,而如果使用lfu演算法,則應該刪除7,因為7被使用的次數最少,只使用了一次。相信這個簡單的例子可以大概說明二者的區別。

這道題比之前那道lru的題目還要麻煩一些,因為那道題只要用個list把數字按時間順序存入,鍊錶底部的位置總是最久未被使用的,每次刪除底部的值即可。而這道題不一樣,由於需要刪除最少次數的數字,那麼我們必須要統計每乙個key出現的次數,所以我們用乙個雜湊表m來記錄當前資料和其出現次數之間的對映,這樣還不夠,為了方便操作,我們需要把相同頻率的key都放到乙個list中,那麼需要另乙個雜湊表freq來建立頻率和乙個裡面所有key都是當前頻率的list之間的對映。由於題目中要我們在o(1)的時間內完成操作了,為了快速的定位freq中key的位置,我們再用乙個雜湊表iter來建立key和freq中key的位置之間的對映。最後當然我們還需要兩個變數cap和minfreq,分別來儲存cache的大小,和當前最小的頻率。

為了更好的講解思路,我們還是用例子來說明吧,我們假設cache的大小為2,假設我們已經按順序put進去5,4,那麼來看一下內部的資料是怎麼儲存的,由於value的值並不是很重要,為了不影響key和frequence,我們採用value#來標記:

m:5 ->

4 ->

freq:

1 ->

iter:

4 -> list.begin() + 1

5 -> list.begin()

這應該不是很難理解,m中5對應的頻率為1,4對應的頻率為1,然後freq中頻率為1的有4和5。iter中是key所在freq中對應鍊錶中的位置的iterator。然後我們的下一步操作是get(5),下面是get需要做的步驟:

1. 如果m中不存在5,那麼返回-1

2. 從freq中頻率為1的list中將5刪除

3. 將m中5對應的frequence值自增1

4. 將5儲存到freq中頻率為2的list的末尾

5. 在iter中儲存5在freq中頻率為2的list中的位置

6. 如果freq中頻率為minfreq的list為空,minfreq自增1

7. 返回m中5對應的value值

經過這些步驟後,我們再來看下此時內部資料的值:

m:5 ->

4 ->

freq:

1 ->

2 ->

iter:

4 -> list.begin()

5 -> list.begin()

這應該不是很難理解,m中5對應的頻率為2,4對應的頻率為1,然後freq中頻率為1的只有4,頻率為2的只有5。iter中是key所在freq中對應鍊錶中的位置的iterator。然後我們下一步操作是要put進去乙個7,下面是put需要做的步驟:

1. 如果呼叫get(7)返回的結果不是-1,那麼在將m中7對應的value更新為當前value,並返回

2. 如果此時m的大小大於了cap,即超過了cache的容量,則:

a)在m中移除minfreq對應的list的首元素的紀錄,即移除4 ->

b)在iter中清除4對應的紀錄,即移除4 -> list.begin()

c)在freq中移除minfreq對應的list的首元素,即移除4

3. 在m中建立7的對映,即 7 ->

4. 在freq中頻率為1的list末尾加上7

5. 在iter中儲存7在freq中頻率為1的list中的位置

6. minfreq重置為1

經過這些步驟後,我們再來看下此時內部資料的值:

m:5 ->

7 ->

freq:

1 ->

2 ->

iter:

7 -> list.begin()

5 -> list.begin()

參考**:

class lfucache 

int get(int key)

void put(int key, int value)

if (m.size() >= cap)

m[key] = ;

freq[1].push_back(key);

iter[key] = --freq[1].end();

minfreq = 1;

}private:

int cap, minfreq;

unordered_map> m;

unordered_map> freq;

unordered_map::iterator> iter;

};

快取策略之LRU和LFU

快取,就是把資料儲存在本地,簡單實用key value做乙個對映就好了。但為什麼還要快取策略,因為快取的大小需要限制,否則儲存的東西只增不減,時間一長就有很多記憶體浪費。因為資源總是有限,所以優化,因為優化所以複雜 這個是least recently used的縮寫,即最近最少使用。它的邏輯很簡單 ...

LFU演算法實現(460 LFU快取)

今天位元組客戶端三面問了這道題,沒做出來。第一,之前沒見過lfu,第二,要求o 1 時間,條件苛刻一點。只能說無緣位元組。言歸正傳,lfu演算法 least frequently used,最近最不經常使用演算法。什麼意思呢 對於每個條目,維護其使用次數cnt 最近使用時間time。cache容量為...

演算法題 LFU快取

題目 設計並實現最不經常使用 lfu 快取的資料結構。它應該支援以下操作 get 和 put。get key 如果鍵存在於快取中,則獲取鍵的值 總是正數 否則返回 1。put key,value 如果鍵不存在,請設定或插入值。當快取達到其容量時,它應該在插入新專案之前,使最不經常使用的專案無效。在此...