LFUアルゴリズムの実装(460. LFUキャッシュ)

今日、バイトクライアントは3つの側面でこの質問をしましたが、それはしませんでした。第一に、私は以前にlfuを見たことがない、そして第二に、それはO(1)時間を必要とし、条件は少し厳しい。逃したバイトだけを言うことができます。

LFUアルゴリズム:使用頻度が最も低く、使用頻度が最も低いアルゴリズム。

意味:エントリごとに、使用数cntと最新の使用時間を維持します。

キャッシュ容量はnです。つまり、最大n個のエントリが格納されます。

次に、新しいエントリを挿入する必要があり、キャッシュがいっぱいになったときに、以前のエントリを削除する必要があります。削除方法は次のとおりです。使用頻度が最も低いエントリcntを最初に削除します。これは、最近最も使用頻度が低いため、削除することです。回数cntの最小値がmin_cntの場合は、このmin_cntに対応するエントリが複数あるので、これらのエントリのうち使用時間最も早いエントリを削除します。リソースは最後の5秒に使用され、bリソースは最後の7秒に使用されます。次に、bリソースが後で使用されるため、aを削除します。したがって、bリソースには、aリソースよりも継続して使用する理由があります。つまり、時間局所性の原則)。

 

lruアルゴリズムの考え方と同様に、リンクテーブルを追加するためにハッシュテーブルが使用されます。リンクされたリストは、時系列順に責任があります。ハッシュテーブルは、O(1)時間でキーに対応するノードを見つける責任があります。

投稿された写真、ソース:https : //leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiwei/

または、ハッシュテーブルを使用して、O(1)時間でキーに対応するノードを見つけます。

さらに、lfuアルゴリズムは、参照カウントと最近の使用時間という2つの次元に従ってソートされるためです。したがって、リンクされたリストだけでは十分ではありません。解決策は、下の図に示すように2番目のハッシュテーブルを使用することです。キーは参照カウントで、値はリンクリストです。現在の使用が現在のキーであるすべてのノードが格納されます。リンクリスト内のすべてのノードは、最新の使用時間に従って並べ替えられ、最も最近使用されたノードがリンクリストの先頭にあり、最新のノードが末尾に使用されています。このようにして、O(1)時間を完了して、キーに対応するノードを(最初のハッシュテーブルを介して)見つけ、O(1)時間を削除してノードを変更(2番目のハッシュテーブルを介して)できます。

 

注:get(クエリ)操作とput(挿入)操作はどちらも「使用済み」と見なされ、どちらも参照カウントを増やします。

したがって、get(キー)操作の実装のアイデア:最初のハッシュテーブルでキーが見つかった場合は、対応するリンクリストノードを取得します。次に、2番目のハッシュテーブルで、参照カウントが+1であるリンクリストの先頭に移動し、前のノードを削除します。

put(キー、値)操作の実装のアイデア:キーが最初のハッシュテーブルにある場合、操作はget(キー)と同じですが、新しいノードの値が新しい値に設定される点が異なります。

キーが見つからない場合は、キャッシュ内のアイテムを削除する必要がある場合があります(容量が制限に達しています)。2番目のハッシュテーブルで参照カウントが最小のリンクリストを直接検索し、エンドノード(最新の使用)を削除します。

次に、新しいノードを追加します。

 

注意事項:1.容量が制限を超えたときにノードを削除する必要がある場合、2番目のハッシュテーブルのエントリを削除すると、最初のハッシュテーブルの対応するマッピングも削除されます。

2.現在の最小参照カウントを保存するには、min_cnt整数変数を保持する必要があります。容量が制限を超えたためにノードを削除する必要がある場合、削除する必要があるノードを見つけるためにO(1)時間必要です。

 

 

 

タイトル:

最も使用頻度の低い(LFU)キャッシュアルゴリズムのデータ構造を設計および実装してください。次の操作をサポートする必要があります:getとput。

get(キー)-キーがキャッシュに存在する場合、キーの値を取得し(常に正の数)、それ以外の場合は-1を返します。
put(key、value)-キーがすでに存在する場合はその値を変更し、キーが存在しない場合はキーと値のペアを挿入します。キャッシュがその容量に達すると、新しいアイテムを挿入する前に、使用頻度の最も低いアイテムを無効にする必要があります。この問題では、同数の場合(つまり、2つ以上のキーが同じ使用頻度を持っている場合)、最も長い未使用のキーを削除する必要があります。
「使用されたアイテムの数」は、アイテムが挿入されてから、それに対してgetおよびput関数が呼び出された回数の合計です。対応するアイテムが削除されると、使用回数は0に設定されます。

 

上級:
O(1)時間の複雑さの中で2つの操作を実行できますか?

 

例:

LFUCacheキャッシュ=新しいLFUCache(2 / *容量(キャッシュ容量)* /);

cache.put(1、1);
cache.put(2、2);
cache.get(1); // 1を返します
cache.put(3、3); //キー2を削除します
cache.get(2); // -1を返す(キー2が見つからない)
cache.get(3); // 3を
返すcache.put(4、4); //キー1を削除する
cache.get(1); // -1を返す(見つからない検索キー1)
cache.get(3); // 3を返します
cache.get(4); // 4を返します

 

回答:

struct p {
     int key、value、cnt; 
    p(int a、int b、int c):key(a)、value(b)、cnt(c){} 
}; 
クラスLFUCache {
 public 
    unordered_map < int、list <p> :: iterator> mp_key; 
    unordered_map < int、list <p >> mp_cnt;
    int min_cnt = 1 ;
    // key、value、cnts 
    int n; 
    LFUCache(int capacity){ 
        n = 容量; 
    } 
    
    int getint key){
         if(n == 0){ return - 1 ;}
         // cout << "get begin"; 
        if(mp_key.count(key)){ // hit 
            auto iter = mp_key [key];
             int cnt = iter-> cnt、val = 
            iter- > value; mp_cnt [cnt + 1 ] .push_front(p(iter-> key、val、cnt + 1)); // cnt + 1のリストの先頭に 
            挿入するmp_cnt [cnt] .erase(iter);     //(min_cnt == cntおよびmp_cnt [cnt] .size()== 0)の場合、前のノードを削除します
             { //更新min_cnt 
                min_cnt ++ ; 
            } 
            mp_key [key] = mp_cnt [cnt + 1 ] .begin();  // 更新mp_key
             // cout << "get end" << endl; 
            戻り値; 
        } 
        // cout << "get end" << endl; 
        リターン - 1 
    } 
    
    void put(int key、int value){
         if(n == 0){ return ;}
         // cout << "get begin"; 
        if(mp_key.count(key)){ // 命中
            auto iter = mp_key [key];
             int cnt = 
            iter- > cnt; mp_cnt [cnt + 1 ] .push_front(p(key、value、cnt + 1 )); 
            mp_cnt [cnt] .erase(iter); 
            mp_key [key] = mp_cnt [cnt + 1 ] .begin();
             if(min_cnt == cnt and mp_cnt [cnt] .size()== 0 ){ 
                min_cnt ++ ; 
            } 
        } 
        else { // 新しいノードを挿入する
            if(mp_key.size( )> = n){ // cntが最小のリスト内
                最も古い(mp_cnt [min_cnt] .back())int を削除する必要があるdeleteKey = mp_cnt [min_cnt] .back()。key; 
                mp_cnt [min_cnt] .pop_back(); 
                mp_key.erase(deleteKey); 
            } 
            mp_cnt [ 1 ] .push_front(p(key、value、1 )); 
            mp_key [key ] = mp_cnt [ 1 ] .begin(); 
            min_cnt = 1 ; // 新しいノードが挿入され、最小cntは1でなければならない
        }
         // cout << "put end" << endl; 
    } 
};

 

おすすめ

転載: www.cnblogs.com/FdWzy/p/12741981.html
おすすめ