認識 LFU 演算法

2021-10-09 03:51:30 字數 4603 閱讀 2315

目錄

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,該keyfreq就要加一。

3、如果在容量滿了的時候進行插入,則需要將freq最小的key刪除,如果最小的freq對應多個key,則刪除其中最舊的那乙個。

好的,我們希望能夠在 o(1) 的時間內解決這些需求,可以使用基本資料結構來逐個擊破:

1、使用乙個hashmap儲存keyval的對映,就可以快速計算get(key)

hashmapkeytoval;
2、使用乙個hashmap儲存keyfreq的對映,就可以快速操作key對應的freq

hashmapkeytofreq;
3、這個需求應該是 lfu 演算法的核心,所以我們分開說。

3.1、首先,肯定是需要freqkey的對映,用來找到freq最小的key

3.2、將freq最小的key刪除,那你就得快速得到當前所有key最小的freq是多少。想要時間複雜度 o(1) 的話,肯定不能遍歷一遍去找,那就用乙個變數minfreq來記錄當前最小的freq吧。

3.3、可能有多個key擁有相同的freq,所以freqkey是一對多的關係,即乙個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 如果鍵不存在,請設定或插入值。當快取達到其容量時,它應該在插入新專案之前,使最不經常使用的專案無效。在此...