LeetCodeブラッシングノート_146。LRUキャッシュメカニズム

トピックはLeetCode
146 から来ています。LRU キャッシングメカニズム

他のソリューションまたはソースコードにアクセスできます:tongji4m3

解説

習得したデータ構造を使用して、LRU(最近使用されていない)キャッシュメカニズムを設計および実装します。次の操作をサポートする必要があります。データの取得とデータの書き込み。

データの取得get(key)-キー(key)がキャッシュに存在する場合は、キーの値(常に正の数)を取得します。それ以外の場合は-1を返します。
データの書き込みput(key、value)-キーがすでに存在する場合は、そのデータ値を変更します。キーが存在しない場合は、「キー/値」のセットを挿入します。キャッシュ容量が上限に達すると、新しいデータを書き込む前に、最も長い未使用のデータ値を削除して、新しいデータ値のためのスペースを確保する必要があります。

上級:

これら2つの操作をO(1)時間の複雑さで完了できますか?

例:


LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得关键字 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得关键字 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

アイデア

  1. 使用できLinkedHashMap、メソッドはカプセル化されています。removeEldestEntryメソッドを書き換えるだけです
  2. 自分でロジックを実装するには、二重リンクリストとハッシュテーブルの組み合わせを使用します。これは、実際には上記の方法1の特定の実装です。

細部

コード

public class LRUCache
{
    
    
    private Map<Integer, Node> map;//存储的是key,Node
    DoubleList cache;//用于保证删除是O(1)
    private int capacity;

    private class Node
    {
    
    
        int key;
        int value;
        Node next;
        Node pre;

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

    private class DoubleList
    {
    
    
        Node first;//头尾虚节点
        Node last;
        int size;

        DoubleList()
        {
    
    
            first = new Node(0, 0);
            last = new Node(0, 0);
            first.next = last;
            last.pre = first;
            size = 0;
        }

        void addFirst(Node node)
        {
    
    
            node.pre=first;
            node.next=first.next;
            first.next.pre=node;
            first.next=node;
            ++size;
        }

        Node remove(Node node)
        {
    
    

            node.pre.next = node.next;
            node.next.pre = node.pre;

            node.pre = null;
            node.next = null;

            --size;

            return node;
        }

        Node removeLast()
        {
    
    
            if(size==0) return null;
            return remove(last.pre);
        }


    }

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

    public int get(int key)
    {
    
    
        if (!map.containsKey(key)) return -1;
        int value = map.get(key).value;
        put(key, value);
        return value;
    }

    public void put(int key, int value)
    {
    
    
        Node node = new Node(key, value);
        if (map.containsKey(key))
        {
    
    
            cache.remove(map.get(key));
            cache.addFirst(node);
        }
        else
        {
    
    
            if (capacity == cache.size)
            {
    
    
                Node last = cache.removeLast();
                map.remove(last.key);//这是为什么Node要存key,val,不能只存val
            }
            cache.addFirst(node);
        }
        map.put(key, node);
    }
}
/**
调用库的实现方法

LinkedHashMap<Integer, Integer>
它是一个将所有Entry节点链入一个双向链表的HashMap
此外,LinkedHashMap可以很好的支持LRU算法
它额外维护了一个双向链表用于保持迭代顺序,该迭代顺序可以是插入顺序,也可以是访问顺序。
 * @author 12549
 */
public class LRUCache extends LinkedHashMap<Integer, Integer>
{
    
    
    private Integer capacity;
    /*
    当accessOrder标志位为true时,表示双向链表中的元素按照访问的先后顺序排列
    当标志位accessOrder的值为false时,表示双向链表中的元素按照Entry插入LinkedHashMap到中的先后顺序排序
    当我们要用LinkedHashMap实现LRU算法时,就需要调用该构造方法并将accessOrder置为true。
    当accessOrder为true时,get方法和put方法都会调用recordAccess方法使得最近使用的Entry移到双向链表的末尾
     */

    public LRUCache(int capacity)
    {
    
    
        super(capacity,0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key)
    {
    
    
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value)
    {
    
    
        super.put(key, value);
    }

    /*
    该方法是用来被重写的,一般地,如果用LinkedHashMap实现LRU算法,就要重写该方法。
    比如可以将该方法覆写为如果设定的内存已满,则返回true,
    这样当再次向LinkedHashMap中putEntry时,
    在调用的addEntry方法中便会将近期最少使用的节点删除掉(header后的那个节点)。
     */

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest)
    {
    
    
        return size()>capacity;
    }
}

おすすめ

転載: blog.csdn.net/weixin_42249196/article/details/108703928