LeetCode破解之URL缓存算法

87 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情

题目说明

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例 1:

输入 ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 输出 [null, null, null, 1, null, -1, null, -1, 3, 4]

解释 LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // 缓存是 {1=1} lRUCache.put(2, 2); // 缓存是 {1=1, 2=2} lRUCache.get(1); // 返回 1 lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3} lRUCache.get(2); // 返回 -1 (未找到) lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} lRUCache.get(1); // 返回 -1 (未找到) lRUCache.get(3); // 返回 3 lRUCache.get(4); // 返回 4

链表+hash

使用匿名内部类的方式初始化对象

第三个参数一定要设定为true,该参数决定是否启用过时删除对象

并且要覆盖removeEldestEntry方法,如果超过size,则移除最老的对象。

思路:使用头结点和尾结点来方便双向链表的插入、删除操作。
读取元素时,如果该元素存在,那么更新双向链表,将该元素对应的节点调整至表头。插入元素时,如果该元素存在,那么更新该元素对应的节点的值并将其调整至表头,如果该元素不存在,那么创建新节点并将其插入表头。
class LRUCache {
    LinkedHashMap<Integer,Integer> map = null;
    public LRUCache(int capacity) {
        map = new LinkedHashMap<Integer,Integer>(capacity,0.75f,true){
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                if(this.size() > capacity){
                    return true;
                }
                return false;
            }
        };
    }
    
    public int get(int key) {
        Integer v = map.get(key);
        return v==null?-1:v.intValue();
    }
    
    public void put(int key, int value) {
        map.put(key,value);
    }
}

注意

由于要存储键值对,所以考虑哈希表,get操作会把某一节点移到头部,put操作当空间满了会删除尾部节点 由于需要对头尾操作,考虑双向链表list,begin、end返回的是第一个元素的指针,front、back返回的是元素的引用

而且当我们采用双向链表作为LRU索引表,采用哈希表作为数据表

如果当容量满时,从索引表移除第一项,再从哈希表删除元素。

过程中每次插入元素或获取元素时,都要从索引表中删除索引,再重新插入索引。