zoukankan      html  css  js  c++  java
  • LeetCode Notes_#146 LRU缓存机制

    LeetCode Notes_#146 LRU缓存机制

    Contents

    题目


    解答

    方法1:哈希表

    感觉这个题目挺有意思,我用HashMap实现了一个基本的解法,能通过除了一个大数据输入用例的其他所有用例,说明逻辑上是没有问题的。但是需要优化时间复杂度。

    class LRUCache {
        HashMap<Integer, Integer> container;
        HashMap<Integer, Integer> noVisitTimes;
        int cap;
        int longestNoUseKey;
    
        public LRUCache(int capacity) {
            container = new HashMap<>();
            noVisitTimes = new HashMap<>();
            cap = capacity;
            longestNoUseKey = -1;
        }
        
        public int get(int key) {
            if(container.size() == 0) return -1;
            for(Integer k: container.keySet()){
                if(k == key) noVisitTimes.put(key, 0);
                else noVisitTimes.put(k, noVisitTimes.getOrDefault(k, 0) + 1);
            }
            if(container.containsKey(key)) return container.get(key);
            return -1; 
        }
        
        public void put(int key, int value) {
            for(Integer k: container.keySet()){
                if(k == key) noVisitTimes.put(key, 0);
                else noVisitTimes.put(k, noVisitTimes.getOrDefault(k, 0) + 1);
            }
            if(container.containsKey(key)){
                container.put(key, value);
            }else{
                if(container.size() < cap){
                    container.put(key, value);
                }else{
                    int maxNoUseTimes = -1;
                    for(Integer k: noVisitTimes.keySet()){
                        if(noVisitTimes.get(k) > maxNoUseTimes){
                            maxNoUseTimes = noVisitTimes.get(k);
                            longestNoUseKey = k;
                        }
                    }
                    container.remove(longestNoUseKey);
                    noVisitTimes.remove(longestNoUseKey);
                    container.put(key, value);
                }
            }
        }
    }

    复杂度分析

    时间复杂度:对于get()put()的操作,每次都需要遍历一遍大小为container.size()hashmap,复杂度为O(n),n为container.size()
    空间复杂度:O(n),借助了一个额外的noVisitTimes的hashmap,大小和container相同

    方法2:哈希表+双向链表

    方法1使用哈希表存储<key, value>的键值对,这里修改为存储<key, DLinkedNode>的键值对。DLinkedNode是双向链表数据结构的节点。
    问题的核心在于: 在缓存区满的情况下,要如何决定删除哪一个内容?当然是删除最旧的内容。
    难点在于: 如何维护缓存内容的“新旧程度”,我们可以用双向链表来表达“新旧程度”,即新的内容在链表头部,旧的内容在链表尾部。
    为什么一定要用双向链表? 主要是在删除内容时,需要删除链表当中的对应节点,如果用单向链表,删除一个节点时,需要给出这个节点的前序节点,比较麻烦。而双向链表的每个节点都是可以直接访问到它的前序节点的,这样删除起来会比较方便。

    class LRUCache {
        //定义双向链表的节点数据结构
        class DLinkedNode{
            int key;
            int value;
            DLinkedNode prev;
            DLinkedNode next;
            public DLinkedNode(){}
            public DLinkedNode(int _key, int _value){
                key = _key;
                value = _value;
            }
        }
        //哈希表->查找缓存内容的“线头”
        private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
        private int size;
        private int capacity;
        private DLinkedNode head, tail;
        //构造方法:初始化所有类变量,构造双向链表的头尾伪节点
        public LRUCache(int capacity) {
            this.size = 0;
            this.capacity = capacity;
            head = new DLinkedNode();
            tail = new DLinkedNode();
            head.next = tail;
            tail.prev = head;
        }
        //根据输入的key查找内容
        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);
            }
        }
        //双向链表的一些方法
        //addToHead:put()新内容的时候,需要把新加入的键值对插入到双向链表的头部,表示这是最新的内容
        private void addToHead(DLinkedNode node){
            node.prev = head;
            node.next = head.next;
            head.next.prev = node;
            head.next = node;
        }
        //这里的removeNode()是removeTail()的工具方法
        private void removeNode(DLinkedNode node){
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
        //put()时更新了一个原有的内容,这个内容变成了最新内容,需要移动到双向链表的头部;get()时查找了某个内容,这个内容也需要移动到双向链表的头部
        private void moveToHead(DLinkedNode node){
            removeNode(node);
            addToHead(node);
        }
        //removeNode():put()新内容后,如果超出了cache的capacity,就需要把最后一个节点(即最旧的内容)删除,
        private DLinkedNode removeTail(){
            DLinkedNode res = tail.prev;
            removeNode(res);
            return res;
        }
    }

    复杂度分析

    时间复杂度:O(1)
    空间复杂度:O(capacity)

  • 相关阅读:
    LNMP一键安装
    IIS出现问题报CS0016
    如何在windows live Write中添加插件
    合同管理系统 功能一览表
    房地产合同档案分类及编号规则
    属性ErrorLogFile不可用于JobServer的解决方案
    Wps定义选中区域的名称
    常用的SQL语句
    xp sp3 访问IIS元数据库失败解决办法
    完全卸载oracle11g步骤
  • 原文地址:https://www.cnblogs.com/Howfars/p/14327325.html
Copyright © 2011-2022 走看看