146.LRU缓存机制

Posted yh-simon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了146.LRU缓存机制相关的知识,希望对你有一定的参考价值。

技术图片
技术图片

思路

  • 用HashMap类型的cache记录Cache的数据

  • 用List类型的record 记录Cache的各个元素使用情况 record有点类似队列,方便起见,下文就以队列称呼。

    • 对于cache中已有的元素: 若进行get put操作,就将该元素移到队列(record)的最后(即表示该数据最近使用过)
    • 对于新数据:先判断队列是否满
      • 队列已满,删除第一个元素 ,剩下的元素往前移,再添加新元素,更新cache
      • 队列未满,队尾添加新元素,更新cache
  • 以上就是大致思路,同时,可以发现每次更新队列时,即元素的插入,移动等操作,效率其实是很低的,所有想起了链表,虽说链表插入数据的效率相对较高,可如何将元素移到尾部,效率还不如集合(数组)。以下代码肯定是需要优化的。

代码

    //不能定义为静态变量  550ms
    int capacity;
    List<Integer> record=new ArrayList<>();
    Map<Integer,Integer> cache=new HashMap<>();
    public LRUCache(int capacity) {
        this.capacity=capacity;
    }

    public int get(int key) {
        if(cache.containsKey(key)){
            int i=record.indexOf(key);
            i++;
            for(;i<record.size();i++){
                record.set(i-1, record.get(i));
            }
            record.set(record.size()-1, key);
            return cache.get(key);
        }
        return -1;
    }

    public void put(int key, int value) {
        if(cache.size()<capacity){
            if(cache.containsKey(key)){
                int i = record.indexOf(key);
                i++;
                for(;i<record.size();i++){
                    record.set(i-1, record.get(i));
                }
                record.set(record.size()-1, key);
            }else{
                record.add(key);
            }
            cache.put(key, value);
        }else{//cache已满
            if(cache.containsKey(key)){
                int i = record.indexOf(key);i++;

                for(;i<record.size();i++){
                    record.set(i-1, record.get(i));
                }
                record.set(capacity-1, key);
                cache.put(key, value);
            }else{
                //移除最久未使用的
                cache.remove(record.get(0));
                //更新队列
                for(int i=1;i<record.size();i++){
                    record.set(i-1, record.get(i));
                }
               record.set(capacity-1, key);
                cache.put(key, value);
            }
        }

    }

链表+哈希表

代码

//使用链表
//19ms  80%
public class LRUCache2 {
    class DLinkedNode{
        int key;
        int value;
        DLinkedNode pre;
        DLinkedNode next;
        public DLinkedNode(){}
        public DLinkedNode(int key,int value){this.key=key;this.value=value;}
    }

    private Map<Integer,DLinkedNode> cache=new HashMap<>();
    private int size;
    private int capacity;
    //使用伪头部和伪尾部节点
    private DLinkedNode head,tail;


    public LRUCache2(int capacity){
        this.size=0;
        this.capacity=capacity;
        head=new DLinkedNode();
        tail=new DLinkedNode();
        head.next=tail;
        tail.pre=head;
    }

    public int get(int key){
        DLinkedNode node=cache.get(key);
        if(node==null) return -1;
        //key存在,先从原位置移除  再移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key,int value){
        DLinkedNode node=cache.get(key);
        if(node==null) {
            //key不存在,创建新节点
            DLinkedNode newNode=new DLinkedNode(key,value);
            //加入哈希表
            cache.put(key, newNode);
            //添加到双向链表头部
            addToHead(newNode);
            size++;
            //如果添加节点后 节点数量超过容量
            if(size>capacity){
                //移除尾部节点
                DLinkedNode tail=removeTail();
                //哈希表移除对应的项
                cache.remove(tail.key);
                size--;
            }
        }else{//key已存在,更新value值
            node.value=value;
            //再移至双向链表的头部
            moveToHead(node);
        }
    }

    private void addToHead(DLinkedNode node){
        node.pre=head;
        node.next=head.next;
        head.next.pre=node;
        head.next=node;
    }
    private void removeNode(DLinkedNode node){
        node.pre.next=node.next;
        node.next.pre=node.pre;
    }

    private void moveToHead(DLinkedNode node){
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail(){
        DLinkedNode res=tail.pre;
        removeNode(res);
        return res;
    }
}

参考链接:

官方题解


以上是关于146.LRU缓存机制的主要内容,如果未能解决你的问题,请参考以下文章

146. LRU缓存机制

LeetCode 146. LRU 缓存机制

146. LRU缓存机制

146. LRU缓存机制

Leetcode No.146 LRU 缓存机制

146.LRU缓存机制