zoukankan      html  css  js  c++  java
  • 146.LRU缓存机制

    image-20200527131536448

    思路

    • 用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;
        }
    }
    
    

    参考链接:

    官方题解

  • 相关阅读:
    Linux 误卸载软件,所有命令不能用了咋办
    MySQL 全局锁和表锁
    MongoDB 基础
    MySQL 常见错误
    MySQL 锁信息和事务
    B2C电子商务平台概述及开发公司推荐
    O2O本地生活平台推荐
    OA办公系统哪个公司做的好
    集团企业OA系统选型推荐
    协同OA办公系统选型推荐
  • 原文地址:https://www.cnblogs.com/yh-simon/p/12994867.html
Copyright © 2011-2022 走看看