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

2022-05-08 21:45:09 字數 2691 閱讀 4768

今天位元組客戶端三面問了這道題,沒做出來。第一,之前沒見過lfu,第二,要求o(1)時間,條件苛刻一點。只能說無緣位元組。

言歸正傳,lfu演算法:least frequently used,最近最不經常使用演算法。

什麼意思呢:對於每個條目,維護其使用次數cnt、最近使用時間time。

cache容量為n,即最多儲存n個條目。

那麼當我需要插入新條目並且cache已經滿了的時候,需要刪除乙個之前的條目。刪除的策略是:優先刪除使用次數cnt最小的那個條目,因為它最近最不經常使用,所以刪除它。如果使用次數cnt最小值為min_cnt,這個min_cnt對應的條目有多個,那麼在這些條目中刪除最近使用時間time最早的那個條目(舉個栗子:a資源和b資源都使用了兩次,但a資源在5s的時候最後一次使用,b資源在7s的時候最後一次使用,那麼刪除a,因為b資源更晚被使用,所以b資源相比a資源來說,更有理由繼續被使用,即時間區域性性原理)。

類似lru演算法的想法,利用雜湊表加鍊錶。鍊錶是負責按時間先後排序的。雜湊表是負責o(1)時間查詢key對應節點的。

還是用乙個雜湊表,用來o(1)時間查詢key對應的節點。

另外由於lfu演算法是按照兩個維度:引用計數、最近使用時間來排序的。所以乙個鍊錶肯定不夠用了。解決辦法就是按照下圖這樣,使用第二個雜湊表,key是引用計數,value是乙個鍊錶,儲存使用次數為當前key的所有節點。該鍊錶中的所有節點按照最近使用時間排序,最近使用的在鍊錶頭部,最晚使用的在尾部。這樣我們可以完成o(1)時間查詢key對應節點(通過第乙個雜湊表);o(1)時間刪除、更改某節點(通過第二個雜湊表)。

注意:get(查詢)操作和put(插入)操作都算「使用」,都會增加引用計數。

所以get(key)操作實現思路:如果第乙個雜湊表中能查到key,那麼取得相應鍊錶節點。接下來在第二個雜湊表中,把它移到其引用計數+1位置的鍊錶頭部,並刪除之前的節點。

put(key,value)操作實現思路:如果第乙個雜湊表中能查詢key,那麼操作和get(key)一樣,只是把新節點的value置為新value。

如果查不到key,那麼我們有可能需要刪除cache中的某一項(容量已經達到限制):直接找到第二個雜湊表中最小引用計數的鍊錶,刪除其末尾節點(最晚使用)。

之後再新增新節點即可。

注意點:1.容量超限需要刪除節點時,刪除了第二個雜湊表中的項的同時,第乙個雜湊表中對應的對映也應該刪掉。

2.需要保持乙個min_cnt整型變數用來儲存當前的最小引用計數。因為容量超限需要刪除節點時,我們需要o(1)時間找到需要刪除的節點。

請你為 最不經常使用(lfu)快取演算法設計並實現資料結構。它應該支援以下操作:get 和 put。

get(key) - 如果鍵存在於快取中,則獲取鍵的值(總是正數),否則返回 -1。

put(key, value) - 如果鍵已存在,則變更其值;如果鍵不存在,請插入鍵值對。當快取達到其容量時,則應該在插入新項之前,使最不經常使用的項無效。在此問題中,當存在平局(即兩個或更多個鍵具有相同使用頻率)時,應該去除最久未使用的鍵。

「項的使用次數」就是自插入該項以來對其呼叫 get 和 put 函式的次數之和。使用次數會在對應項被移除後置為 0 。

高階:你是否可以在 o(1) 時間複雜度內執行兩項操作?

示例:lfucache cache = new lfucache( 2 /* capacity (快取容量) */ );

cache.put(1, 1);

cache.put(2, 2);

cache.get(1); // 返回 1

cache.put(3, 3); // 去除 key 2

cache.get(2); // 返回 -1 (未找到key 2)

cache.get(3); // 返回 3

cache.put(4, 4); // 去除 key 1

cache.get(1); // 返回 -1 (未找到 key 1)

cache.get(3); // 返回 3

cache.get(4); // 返回 4

structp};

class

lfucache

intget(int

key)

//cout<

if(mp_key.count(key))

mp_key[key]=mp_cnt[cnt+1].begin(); //

更新mp_key

//cout<

val;

}//cout<

; }

void put(int key, int

value)

//cout<

if(mp_key.count(key))

}else

mp_cnt[

1].push_front(p(key,value,1

)); mp_key[key]=mp_cnt[1

].begin();

min_cnt=1;//

插入新節點了,最小cnt一定是1

LEETCODE 460 實現LFU快取

方法一 雙雜湊表法 題目要求需要在常數時間完成插入和查詢演算法,聯絡之前的lru演算法,很容易想到用雜湊表。具體做法如下 第乙個雜湊表儲存 使用頻率freq和乙個雙向鍊錶這樣一對對映,鍊錶的每個節點儲存key value和freq 使用頻率 第二個雜湊表儲存freq和鍊錶中的乙個節點這樣一對對映 1...

演算法題 LFU快取

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

LFU快取策略

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 ...