146:LRU缓存机制

问题描述

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

  • 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
  • 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:

  • 你是否可以在 O(1) 时间复杂度内完成这两种操作?

思路

这题就是:如果要更新的key在这个数据结构中,那么就更新这个值。 如果不在,则看看容量满了没有,如果满了,就先删除一个最后使用的K-V对。最后加入新的KV。 为了方便,我们用lastRead值和一个不断自增的全局的count来标记最后访问的计数。
什么叫最后访问呢?
更新值,读取值,插入值 都算最后访问。(方法一)

方法一虽然简单明了,但是不符合题意说的进阶O(1)。我曾经一度以为这是不可能O(1)的。但是这题有明显的特点。我们用链表可以试一试。数据结构是这样的:
我们维持一个HashMap, HashMap中的value是存储的结点。所有HashMap中的结点构成了一条链表。 那么,HashMap查找的时间是O(1),但是别忘了我们还要更新链表呢。删除的时间呢? 由于单链表删除之前要查找,所以时间也是O(n)。 怎么样才能让删除也是O(1)呢? 双向链表就可以做到这点。 我们设置好head,tail。做好指向关系(head.next = tail. tail.pre = head)。这样我们对于所有的结点都能归一化处理了。(方法二)
当然啦,这种称为有序HashMap的表在java中有实现了,叫做LinkedHashMap。可以直接用它做。(方法三)

方法一

class Value{
    int val;
    int lastRead;
    Value(int val){
        this.val = val;
    }
}
class LRUCache {
    Map<Integer,Value> map;
    int length;
    int count;
    public LRUCache(int capacity) {
        length = capacity;
        map = new HashMap<>(capacity);
    }

    public int get(int key) {
        if(map.containsKey(key)){
            map.get(key).lastRead = count++;
            return map.get(key).val;
        }
        return -1;
    }

    public void put(int key, int value) {
        if(get(key) != -1) {
            map.get(key).val = value;
            return;
        }
        if(map.size() == length){
            // 达到最大容量
            int targetKey = -1;
            int minLastRead = Integer.MAX_VALUE;
            for(Integer k: map.keySet()){
                if(minLastRead > map.get(k).lastRead){
                    targetKey = k;
                    minLastRead = map.get(k).lastRead;
                }
            }
            map.remove(targetKey);
        }
        if(map.size() < length){
            map.put(key,new Value(value));
            map.get(key).lastRead = count++;
        }
    }
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

方法二

class Value{
    int key;
    int val;
    Value(int key,int val){
        this.key = key;
        this.val = val;
    }
    Value pre;
    Value next;
}
class LRUCache {
    Value head;
    Value tail;
    Map<Integer,Value> map;
    int length;
    public LRUCache(int capacity) {
        head = new Value(-1,-1);
        tail = new Value(-1,-1);
        head.next = tail;
        tail.pre = head;
        length = capacity;
        map = new HashMap<>(capacity);
    }

    public int get(int key) {
        if(map.containsKey(key)){
            // 将对应的结点移动到tail
            map.get(key).pre.next = map.get(key).next;
            map.get(key).next.pre = map.get(key).pre;
            tail.pre.next = map.get(key);
            map.get(key).pre = tail.pre;
            map.get(key).next = tail;
            tail.pre = map.get(key);
            return map.get(key).val;
        }
        return -1;
    }

    public void put(int key, int value) {
        if(get(key) != -1 && map.containsKey(key)){
            // 如果这个key在LRU中
            map.get(key).val = value;
        }else{
            // key不在LRU中,需要判定长度
            if(map.size() == length){
                // 满了,删除头结点后面的结点
                int deleteKey = head.next.key;
                head.next.next.pre = head;
                head.next = head.next.next;
                map.remove(deleteKey);
            }
            // 直接加入末尾
            Value tmp = new Value(key,value);
            tmp.pre = tail.pre;
            tmp.next = tail;
            tmp.pre.next = tmp;
            tail.pre = tmp;
            map.put(key,tmp);
        }
    }
}

方法三

class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int capacity;
    
    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);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}
发布了464 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41687289/article/details/105326028