leetcode刷题记录(七十二)——146. LRU 缓存

(一)问题描述

146. LRU 缓存 - 力扣(LeetCode)146. LRU 缓存 - 请你设计并实现一个满足 LRU (最近最少使用) 缓存 [https://baike.baidu.com/item/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) 的平均时间复杂度运行。 示例:输入["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); // 返回 1lRUCache.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); // 返回 3lRUCache.get(4); // 返回 4 提示: * 1 <= capacity <= 3000 * 0 <= key <= 10000 * 0 <= value <= 105 * 最多调用 2 * 105 次 get 和 puthttps://leetcode.cn/problems/lru-cache/description/?envType=study-plan-v2&envId=top-100-liked

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity)正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 O(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

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 105getput

(二)解决思路

这道题以方便要用哈希表方便查找,另一方面可以用双向链表来记录节点被使用的顺序:新增的元素和刚刚被修改了value的元素放在头部,如果插入时节点数量超过了capacity,就把尾部元素删掉。使用双向链表和使用单向链表相比便于操作尾部元素。

方法一:使用现成的数据结构

python和java中都有存在哈希功能的链表结构,python是OrderedDict,java是LinkedHashMap。但是直接用已有的数据结构一般不会符合面试官的要求,和库函数的使用一样,有些数据结构的使用也要慎重,像这种直接用数据结构的情况明显是跳过了问题想要考察的重点

下面的代码来自Leetcode官方题解。

java 复制代码
class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int capacity;
    
    public LRUCache(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}

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

面试官会更希望我们自己实现哈希表+双向链表的功能。思路就是本节开头的那段话。链表的定义其实就是节点类的定义。为了方便插入和删除头尾元素,可以创建虚拟头尾节点head和tail。

java 复制代码
class LRUCache {

    //双向链表
    class DLinkedNode{
        public int key;
        public int value;
        public DLinkedNode prev;
        public DLinkedNode next;

        public DLinkedNode(){};
        public DLinkedNode(int _key,int _value){
           key=_key;
           value=_value;
        }
    }

    //哈希表
    private HashMap<Integer,DLinkedNode> cache=new HashMap<>();

    //size是现在链表的长度,capacity是允许的最大节点数
    private int size;
    private int capacity;

    //虚拟头尾节点
    private DLinkedNode head,tail;

    public LRUCache(int capacity) {
        //大部分时候访问类自身的成员变量不需要this
        //但是如果方法里有某个局部变量和成员变量名字相同,就需要用this区分
        this.size=0;
        this.capacity=capacity;
        head=new DLinkedNode();
        tail=new DLinkedNode();
        head.next=tail;
        tail.prev=head;
    }
    
    public int get(int key) {

        //判断map里是否存在key,如果不存在的话会返回null
        DLinkedNode node=cache.get(key);
        if(node==null){
            return -1;
        }
        else{
            //刚刚访问过的节点移动到头部
            moveToHead(node);
            return node.value;
        }
    }
    
    public void put(int key, int value) {
      //判断map里是否存在key,如果不存在的话会返回null
      //如果只是map存或者更新value的话不需要判断,但这里还涉及链表的变化,所以要判断
      DLinkedNode node=cache.get(key);
      if(node==null){
         //新节点加入
         DLinkedNode newNode=new DLinkedNode(key,value);
         //新节点放在链表头
         putToHead(newNode);
         //节点放进map
         cache.put(key,newNode);
         size++;
         if(size>capacity){
            //超出容量,删除尾部节点
            int removeKey=removeFromTail();
            cache.remove(removeKey);
         }
      }
      else{
        //已经存在的节点更新值
        node.value=value;
        //放在节点头
        moveToHead(node);
      }
    }

    //节点的移动/添加就是指针指向的变化
    public void moveToHead(DLinkedNode node){
          node.prev.next=node.next;
          node.next.prev=node.prev;
          node.next=head.next;
          head.next.prev=node;
          head.next=node;
          node.prev=head;
    }

    public void putToHead(DLinkedNode newNode){
         head.next.prev=newNode;
         newNode.next=head.next;
         head.next=newNode;
         newNode.prev=head;
    }
    public int removeFromTail(){
          DLinkedNode node=tail.prev;
          node.next.prev=node.prev;
          node.prev.next=node.next;
          return node.key;
    }
}

注:大部分时候访问类自身的成员变量不需要this,但是如果方法里有某个局部变量和成员变量名字相同,就需要用this区分。

相关推荐
B站计算机毕业设计超人2 分钟前
计算机毕业设计Django+LSTM模型弹幕情感分析 B站视频数据可视化 B站爬虫 机器学习 深度学习 NLP自然语言处理 大数据毕业设计
大数据·深度学习·算法·机器学习·网络爬虫·课程设计·数据可视化
江木1239 分钟前
论文Learning Efficient Convolutional Networks through Network Slimming(剪枝相关)
算法·机器学习·剪枝
spe143710 分钟前
【零基础学Java】基础文件的创建编写和运行
java·开发语言
马立杰21 分钟前
H3CNE-15-RIP协议
算法·h3cne
DARLING Zero two♡21 分钟前
【初阶数据结构】探索数据的多米诺链:单链表
c语言·数据结构·c++·链表·单链表
DogDaoDao22 分钟前
leetcode 面试经典 150 题:合并区间
c++·算法·leetcode·面试·vector·引用·合并区间
明神特烦恼34 分钟前
2.用户登录流程
java·开发语言·spring boot
__Benco42 分钟前
OpenHarmony API 设计规范
java·大数据·数据库·harmonyos
计算机-秋大田1 小时前
校园二手交易平台小程序设计与实现(LW+源码+讲解)
java·前端·后端·微信小程序·小程序·课程设计