• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
xiaoyaovo
博客园    首页    新随笔    联系   管理    订阅  订阅
剑指 Offer II 31. (LRU)最近最少使用缓存(双向链表 + 哈希表 / LinkedHashMap)

题目

  • 链接 剑指 Offer II 031. 最近最少使用缓存
  • 描述

运用所掌握的数据结构,设计和实现一个 LRU (Least Recently Used,最近最少使用) 缓存机制 。

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存 int get(int key)
  • 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 void put(int key, int value)
  • 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
  • 输入与输出
    在这里插入图片描述

思路

  • 双向链表 + 哈希表
  1. 想到存储的方式是键值对,那么离 哈希表 就不远了;
  2. 但想一想,有个 缓存记录!也就是说,给定一个固定的空间,如果空间满了,删除最不常用的。这个最不常用的判断就是 插入得早,没被 get 过;
  3. 而 哈希表 的顺序并不能用代码控制,因此需要一个辅助的数据结构来完成;
  4. 链表的作用是 记录常用、新增与不常用的键值对,常用、新增放在头部,不常用放在尾部;
  • Java 标准库内置 API LinkedHashMap
  1. 思路都是一样,但是 LinkedHashMap 能够保证插入是有序的;
  2. LinkedHashMap 新增与不常用的键值对,常用、新增放在尾部(尾部尾部尾部),不常用放在头部

还是看代码,代码中分析的更好一些!!!

代码

  • 自己实现双向链表
class LRUCache {
	// 存储 key, Node 而不是 key,value 
	// 方便 get() 方法中 map.get(key).val;
    Map<Integer,Node> map;
	// 用来判断元素常用或者不常用 
    DoubleList list;
	// 用于判断当前空间是否满了
    int size;
    
    // 初始化
    public LRUCache(int capacity) {
        map = new HashMap<>();
        list = new DoubleList();
        size = capacity;
    }
    
    // 返回 map 中 key - node 键值对 中 node.val
    public int get(int key) {
    	// 不包含返回 -1
        if (!map.containsKey(key)) {
            return -1;
        }
		// 更新该元素的常用程度 
		// 进入 put 方法第一个分支
        put(key,map.get(key).val);
        return map.get(key).val;
    }
	    
    // put 要考虑 3 中情况
    // 1. 如果包含 key, 则更新 map 中的值与链表中的顺序
    // 2. 不包含 key, 但是空间已满, 删除链表最后一个元素(不常用), 执行第 3 步
    // 3. 不包含 key, 空间没满, 直接插入 map 和链表中即可
    public void put(int key, int value) {
        Node node = new Node(key,value);
        if (map.containsKey(key)) {
            list.remove(map.get(key));
            list.addFirst(node);
            map.put(key,node);
        }else {
            if (list.size() == size) {
                Node last = list.removeLast();
                map.remove(last.key);
            }
            map.put(key,node);
            list.addFirst(node);
        }
    }
}

// 双向链表类中, 保证了时间复杂度都是 O(1)
class DoubleList {
    Node head;
    Node tail;
    int size;
    
    void addFirst(Node node) {
        if (head == null) {
            head = node;
            tail = node;
        }else {
            node.next = head;
            head.prev = node;
            head = node;
        }
        size++;
    }
    
    void remove(Node node) {
        if (head == node && tail == node) {
            head = null;
            tail = null;
        }else if (node == tail) {
            tail = node.prev;
            node.prev = null;
            tail.next = null;
        }else if (node == head) {
            head = head.next;
            head.prev = null;
            node.next = null;
        }else {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
        size--;
    }
    
    Node removeLast() {
        Node res = tail;
        remove(res);
        return res;
    }
    
    int size() {
        return size;
    }
}

// 创建节点
class Node { 
    int key;
    int val;
    Node next;
    Node prev;
    
    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}
  • Java 标准库内置 API LinkedHashMap
class LRUCache {
    // 初始化
    LinkedHashMap<Integer,Integer> map;
    // 判断空间是否满了
    int size;
    
    
    public LRUCache(int capacity) {
        map = new LinkedHashMap<>();
        size = capacity;
    }
    
    // map 不包含 key, 返回 -1
    // map 包含key, 删除重新添加到尾巴节点
    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        
        int val = map.remove(key);
        map.put(key,val);
        return val;
    }
    
    // 1. 包含则删除并更新, 放在最后一个节点
    // 2. 不包含则添加
    // 3. 如果空间满了, 那么删除头结点
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            map.remove(key);
            map.put(key,value);
        }else {
            map.put(key,value);
            if (map.size() > size) {
                map.remove(map.entrySet().iterator().next().getKey());
            }
        }
    }
}

posted on 2021-08-24 18:14  豆本豆红枣豆奶  阅读(19)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3