Javaデータ構造とアルゴリズムの要約(35)-LRUアルゴリズムの原理と実装

元のリンク

LRUとは

最近のコンピュータでは、メモリは依然として非常に高価であるため、限られたメモリを適切に使用および管理してユーザーにパフォーマンスを向上させる場合、それは意味のある問題です。

LRU(最近使用されていない)は最も最近使用されていないもので、これは典型的なメモリ除去メカニズムです。

素人の言葉で言えば、LRUアルゴリズムは、最近頻繁にアクセスされるデータの保持率が高くなり、ほとんどアクセスされないデータが排除されると考えています。

LRUアルゴリズムの実装のアイデア

LRUアルゴリズムの概念によれば、次のものが必要です。

最大容量としてのパラメータキャップ

データを保存するためのデータ構造であり、必要なもの1.最新のアクセスデータを簡単に更新します。2.使用頻度の最も低いデータを簡単に見つけて、上限に達したらクリーンアップします。

ここで使用するデータ構造は、ハッシュマップ+二重リンクリストです。

1.ハッシュマップのgetメソッドとputメソッドO(1)の時間計算量を使用して、データをすばやくフェッチして保存します。

2. doublelinkedlistの機能(特定のノードの前後のノードにアクセスできます)を使用して、O(1)のデータの追加と削除を実現します。

以下に示すように:

LRUアルゴリズムの原理と実現

 

key2が再び使用されると、対応するnode3がリンクリストの先頭に更新されます。

LRUアルゴリズムの原理と実現

 

cap = 3とすると、key4が作成されてアクセスされると、リンクリストの最後にあるnode2が削除され、key1がクリアされます。

LRUの簡単な実装

ノードノード、ストアキー、値、プレノード、ポストノード

class Node{
    public int key;
    public int val;
    public Node next;
    public Node previous;

    public Node() {
    }

    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}

二重にリンクされたリスト。属性は、サイズ、ヘッドノード、およびテールノードです。

APIを提供します:

  • addFirst():最初のメソッドをリンクリストに挿入します
  • remove():最後のノードを削除します
  • remove(ノードノード):特定のノードを削除します
  • size():リンクリストの長さを取得します
class DoubleList{
    private int size;
    private Node head;
    private Node tail;

    public DoubleList() {
        this.head = new Node();
        this.tail = new Node();
        size = 0;
        head.next = tail;
        tail.previous = head;
    }

    public void addFirst(Node node){
        Node temp = head.next;
        head.next = node;
        node.previous = head;
        node.next = temp;
        temp.previous = node;
        size++;
    }

    public void remove(Node node){
        if(null==node|| node.previous==null|| node.next==null){
            return;
        }

        node.previous.next = node.next;
        node.next.previous = node.previous;
        node.next=null;
        node.previous=null;
        size--;
    }

    public void remove(){
        if(size<=0) return;
        Node temp = tail.previous;
        temp.previous.next = temp.next;
        tail.previous = temp.previous;
        temp.next = null;
        temp.previous=null;
        size--;
    }

    public int size(){
        return size;
    }
}

 

LRUアルゴリズム実装クラス

API

  • get(int key):nullの場合は-1を返します
  • put(int key、int value)マップに存在する場合は、元のノードを削除し、新しいノードを追加します。マップに存在しない場合は、マップとリンクリストに新しいデータを追加します。
public class LRUCache {

    Map<Integer,Node> map;
    DoubleList cache;
    int cap;


    public LRUCache(int cap) {
        map = new HashMap<>();
        cache = new DoubleList();
        this.cap = cap;
    }

    public int get(int key){
        Node node = map.get(key);
        return  node==null? -1:node.val;

    }

    public void put(int key, int val){
        Node node = new Node(key,val);
        if(map.get(key)!=null){
            cache.remove(map.get(key));
            cache.addFirst(node);
            map.put(key,node);
            return;
        }

        map.put(key,node);
        cache.addFirst(node);
        if(cache.size()>cap){
            cache.remove();
        }

    }

    public static void main(String[] args) {
        //test, cap = 3
        LRUCache lruCache = new LRUCache(3);
        lruCache.put(1,1);
        lruCache.put(2,2);
        lruCache.put(3,3);
        //<1,1>来到链表头部
        lruCache.put(1,1);
        //<4,4>来到链表头部, <2,2>被淘汰。
        lruCache.put(4,4);
    }

}

LRUアプリケーションシナリオ

  • 低レベルのメモリ管理、ページ置換アルゴリズム
  • 一般的なキャッシュサービス、memcache \ redisなど
  • ビジネスシナリオの一部

参照

LRU戦略の詳細な説明と実装

LRUの原則とアプリケーションのシナリオ

著者:ZzAllenZz

ソース:https://segmentfault.com/a/1190000039256321

おすすめ

転載: blog.csdn.net/lsx2017/article/details/114040825