美文网首页
LRU缓存结构解题过程

LRU缓存结构解题过程

作者: 新手村的0级玩家 | 来源:发表于2020-12-31 16:50 被阅读0次

0.问题描述

题目描述
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

样例:
输入:[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
输出:[1,-1]


1.问题分析

LRU全称是Least Recently Used,即最近最久未使用的意思。

LRU算法的设计原则是
如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

 思路如下

前面的get和set操作,用Map很容易实现,
难点在于:当`缓存大小`超过K时,如何移除`最不经常使用`的记录,即移除`最久没有被访问`的key和value

找到`最久没有被访问`的key和value 要求
1.知道所有key的`最近`访问时间,能筛选出最小的访问时间
2.获取最小的访问时间对应的key
3.根据key删除

以样例来分析,
输入:[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3(容量)
输出:[1,-1]
[1,1,1]  set(1,1)此时缓存为
key    value    timeInMillis
1          1          1
------------------------------
[1,2,2] set(2,2)此时缓存为
key    value    timeInMillis
1          1          1
2          2          2
------------------------------
[1,3,2]set(3,2)此时缓存为
key    value    timeInMillis
1          1          1
2          2          2
3          2          3
------------------------------
[2,1]get(1)key =1存在,返回对应value= 1
 此时缓存为
key    value    timeInMillis
1          1          4
2          2          2
3          2          3
------------------------------
[1,4,4]set(4,4)此次缓存已满
其中timeInMillis最小值对应的key=2,移除相应的key
再执行set(4,4)添加
此时缓存为
key    value    timeInMillis
1          1          4
3          2          3
4          4          5
------------------------------
[2,2]]get(2)key =2不存在,返回-1, 此时缓存未发生改变
key    value    timeInMillis
1          1          4
3          2          3
4          4          5

2.问题解决

   /**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU(int[][] operators, int k) {
        //存结果的map
        Map<Integer, Integer> keyValueMap = new HashMap<>();
        //存最近访问时间的map
        //最近访问的时间,key
        Map<Long, Integer> timeKeyMap = new HashMap<>();
        //key,最近访问的时间
        Map<Integer, Long> keyTimeMap = new HashMap<>();

        //返回值
        ArrayList<Integer> resultArrayList = new ArrayList<>();
        long timeInMillis = 1;
        for (int[] operator : operators) {
            int opt = operator[0];
            int key = operator[1];
            //    若opt=1,接下来两个整数x, y,表示set(x, y)
            //    若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
            if (1 == opt) {
                int value = operator[2];

                //记录新的时间
                timeInMillis++;

                if (keyValueMap.containsKey(key)) {//已经存在了
                    //移除旧的入库时间对应的key
                    Long keyTimeValue = keyTimeMap.get(key);
                    timeKeyMap.remove(keyTimeValue, key);


                    timeKeyMap.put(timeInMillis, key);
                    keyTimeMap.put(key, timeInMillis);
                    keyValueMap.put(key, value);//更新key对应的value
                } else {//不存在了

                    timeKeyMap.put(timeInMillis, key);
                    keyTimeMap.put(key, timeInMillis);
                    keyValueMap.put(key, value);//添加新的key和value
                    //添加后,超过了最大容量,则直接移除一个
                    if (keyValueMap.keySet().size() > k) {
                        Set<Long> longs = timeKeyMap.keySet();
                        Long minTimes = Collections.min(longs);
                        Integer minKey = timeKeyMap.get(minTimes);
                        timeKeyMap.remove(minTimes, minKey);
                        keyTimeMap.remove(minKey, minTimes);
                        keyValueMap.remove(minKey);
                    }
                }
            } else if (2 == opt) {
                if (keyValueMap.containsKey(key)) {//存在
                    //返回结果
                    resultArrayList.add(keyValueMap.get(key));
                    //移除旧的入库时间对应的key
                    Long keyTimeValue = keyTimeMap.get(key);
                    timeKeyMap.remove(keyTimeValue, key);
                    //记录新的时间
                    timeInMillis++;
                    timeKeyMap.put(timeInMillis, key);
                    keyTimeMap.put(key, timeInMillis);
                } else {//不存在
                    //返回结果
                    resultArrayList.add(-1);
                }
            }
        }
        int[] result = new int[resultArrayList.size()];
        for (int i = 0; i < resultArrayList.size(); i++) {
            Integer integer = resultArrayList.get(i);
            result[i] = integer;
        }
        return result;
    }

结果提示通过,但是很明显,太浪费空间了,且看上去很繁琐

3.优化

3.1引入新的数据结构

针对难点:当`缓存大小`超过K时,如何移除`最不经常使用`的记录,即移除`最久没有被访问`的key和value
可以可以考虑用LinkedHashMap

在LinkedHashMap订购模式中设置参数为true
LinkedHashMap<Integer,Integer> map = new LinkedHashMap<>(6,0.75f,true);

表示让 linkedHashMap `按照访问顺序来进行排序`,`最近访问`的放在`尾部`,`最长时间未被访问`的数据的放在`头部`。

3.2代码实现

/**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU2(int[][] operators, int k) {
        //true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在尾部,最长时间未被访问的数据的放在头部。
        LinkedHashMap<Integer, Integer> urlMap = new LinkedHashMap<Integer, Integer>((int) Math.ceil(k / 0.75) + 1, 0.75f, true);
        //返回值
        ArrayList<Integer> resultArrayList = new ArrayList<>();
        for (int[] operator : operators) {
            int opt = operator[0];
            int key = operator[1];
            if (1 == opt) {//set
                //    若opt=1,接下来两个整数x, y,表示set(x, y)
                int value = operator[2];
                if (urlMap.size() >= k) {//当前已满的状态
                    urlMap.remove(urlMap.keySet().iterator().next());//先移除头部(最长时间未被访问的数据)
                }
                urlMap.put(key, value);
            } else if (2 == opt) {//get
                //    若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
                if (urlMap.keySet().contains(key)) {//存在
                    resultArrayList.add(urlMap.get(key));
                } else {//不存在
                    resultArrayList.add(-1);
                }
            }
        }
        int[] result = new int[resultArrayList.size()];
        for (int i = 0; i < resultArrayList.size(); i++) {
            Integer integer = resultArrayList.get(i);
            result[i] = integer;
        }
        return result;
    }

相关文章

  • LRU缓存结构解题过程

    0.问题描述 题目描述设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能set(key, ...

  • 算法笔记——LRU和LFU缓存结构

    LRU缓存结构 问题描述: 设计可以变更的缓存结构(LRU)【题目】设计一种缓存结构,该结构在构造时确定大小,假设...

  • LeetCode-146- LRU 缓存机制

    LRU 缓存机制 题目描述:运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。实现 ...

  • LeetCode146 动手实现LRU算法

    146. LRU缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持...

  • 算法第4天:LRU缓存机制

    leetcode 146. LRU缓存机制 middle 运用你所掌握的数据结构,设计和实现一个 LRU (最...

  • LeetCode热门100题算法和思路(day6)

    LeetCode 146 LRU缓存 题目详情 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) ...

  • 设计LRU缓存结构

    这道题想了一天,自己做的只通过90%,不知道错在哪里。 答案用了双向链表 get()函数:先用iter =mp[ ...

  • 力扣(LeetCode) -146 LRU缓存机制

    本题考察的LRU缓存机制,HashMap和链表 题目描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近...

  • YYMemoryCache

    YYMemoryCache是内存缓存,所以存取速度非常快,主要用到两种数据结构的LRU淘汰算法 1.LRU Cac...

  • Android 内存缓存 LruCache 原理与实现

    之前经常听到okhttp和glide都使用的lru缓存,那什么是lru缓存呢?android 又是如何实现lru缓...

网友评论

      本文标题:LRU缓存结构解题过程

      本文链接:https://www.haomeiwen.com/subject/dstroktx.html