目錄
1、leetcode460. lfu快取
2、思路分析
3、**實現
3.1、get 方法
3.2、put 方法
3.3、increasefreq方法(核心)
3.4、removeminfreq方法(核心)
3.5、完整**
請你為 最不經常使用(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
一定先從最簡單的開始,根據 lfu 演算法的邏輯,我們先列舉出演算法執行過程中的幾個顯而易見的事實:
1、呼叫get(key)
方法時,要返回該key
對應的val
。
2、只要用get
或者put
方法訪問一次某個key
,該key
的freq
就要加一。
3、如果在容量滿了的時候進行插入,則需要將freq
最小的key
刪除,如果最小的freq
對應多個key
,則刪除其中最舊的那乙個。
好的,我們希望能夠在 o(1) 的時間內解決這些需求,可以使用基本資料結構來逐個擊破:
1、使用乙個hashmap
儲存key
到val
的對映,就可以快速計算get(key)
。
hashmapkeytoval;
2、使用乙個hashmap
儲存key
到freq
的對映,就可以快速操作key
對應的freq
。
hashmapkeytofreq;
3、這個需求應該是 lfu 演算法的核心,所以我們分開說。
3.1、首先,肯定是需要freq
到key
的對映,用來找到freq
最小的key
。
3.2、將freq
最小的key
刪除,那你就得快速得到當前所有key
最小的freq
是多少。想要時間複雜度 o(1) 的話,肯定不能遍歷一遍去找,那就用乙個變數minfreq
來記錄當前最小的freq
吧。
3.3、可能有多個key
擁有相同的freq
,所以freq
對key
是一對多的關係,即乙個freq
對應乙個key
的列表。
3.4、希望freq
對應的key
的列表是存在時序的,便於快速查詢並刪除最舊的key
。
3.5、希望能夠快速刪除key
列表中的任何乙個key
,因為如果頻次為freq
的某個key
被訪問,那麼它的頻次就會變成freq+1
,就應該從freq
對應的key
列表中刪除,加到freq+1
對應的key
的列表中。
hashmap> freqtokeys;
int minfreq = 0;
介紹一下這個linkedhashset
,它滿足我們 3.3,3.4,3.5 這幾個要求。你會發現普通的鍊錶linkedlist
能夠滿足 3.3,3.4 這兩個要求,但是由於普通鍊錶不能快速訪問鍊錶中的某乙個節點,所以無法滿足 3.5 的要求。
linkedhashset
顧名思義,是鍊錶和雜湊集合的結合體。鍊錶不能快速訪問鍊錶節點,但是插入元素具有時序;雜湊集合中的元素無序,但是可以對元素進行快速的訪問和刪除。
那麼,它倆結合起來就兼具了雜湊集合和鍊錶的特性,既可以在 o(1) 時間內訪問或刪除其中的元素,又可以保持插入的時序,高效實現 3.5 這個需求。
綜上,我們可以寫出 lfu 演算法的基本資料結構:
class lfucache
public int get(int key) {}
public void put(int key, int val) {}
}
判斷鍵值是否存在?
n:直接返回-1
y:更新操作頻次+1,返回val
1、判斷表容量是否為0?y:返回;n:goto第2步
2、判斷鍵值是否存在?
y:更新key - value,更新操作頻次+1,返回
n:goto第3步
3、判斷表是否已滿?y:刪除最小頻次的key
4、將key-value,key-freq(1),freq-keys存入表中
5、更新minfreq=1
1、取出key對應的操作頻次freq,並修改freq+1重新壓入表中
2、根據頻次freq取出對應的鍵值集合keyset,判斷keyset是否僅有乙個元素?
y:刪除頻次freq對應的對映,判斷freq是否等於minfreq?y:更新minfreq為minfreq+1;n:goto第3步
n:將key從keyset中刪除
3、取出freq+1對應的鍵值集合keyset(new),沒有對映則建立,取出後將key新增到keyset中
1、取出最小頻次minfreq對應的鍵值集合keyset
2、取出keyset的首個元素delkey,借助迭代器
3、判斷keyset首付僅剩下乙個元素?
y:刪除對映
n:將delkey從keyset集合刪除
4、將delkey從keytoval、keytofreq表中刪除
class lfucache
private void increasefreq(int key)
}else
freqtokeys.putifabsent(freq+1,new linkedhashset());
freqtokeys.get(freq+1).add(key);
} private void removeminfreq()else
keytoval.remove(delkey);
keytofreq.remove(delkey);
} public int get(int key)
increasefreq(key);
return keytoval.get(key);
} public void put(int key, int value)
if(keytoval.containskey(key))
if(keytoval.size() == cap)
keytoval.put(key,value);
keytofreq.put(key,1);
freqtokeys.putifabsent(1,new linkedhashset<>());
freqtokeys.get(1).add(key);
minfreq = 1;
}}
LFU演算法實現(460 LFU快取)
今天位元組客戶端三面問了這道題,沒做出來。第一,之前沒見過lfu,第二,要求o 1 時間,條件苛刻一點。只能說無緣位元組。言歸正傳,lfu演算法 least frequently used,最近最不經常使用演算法。什麼意思呢 對於每個條目,維護其使用次數cnt 最近使用時間time。cache容量為...
LRU 與 LFU 演算法
lru是最近最少使用頁面置換演算法 least recently used 也就是首先淘汰最長時間未被使用的頁面 lfu是最近最不常用頁面置換演算法 least frequently used 也就是淘汰一定時期內被訪問次數最少的頁 比如,第二種方法的時期t為10分鐘,如果每分鐘進行一次調頁,主存塊...
演算法題 LFU快取
題目 設計並實現最不經常使用 lfu 快取的資料結構。它應該支援以下操作 get 和 put。get key 如果鍵存在於快取中,則獲取鍵的值 總是正數 否則返回 1。put key,value 如果鍵不存在,請設定或插入值。當快取達到其容量時,它應該在插入新專案之前,使最不經常使用的專案無效。在此...