【问题描述】

LFU
运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?示例:LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

【解答思路】

1. 缓存是有限的,在缓存满的时候,删除哪些元素,就有不同的缓存删除策略。

LRU (Least Recently Used)缓存机制

  • 在缓存满的时候,删除缓存里最久未使用的数据,然后再放入新元素;
  • 数据的访问时间很重要,访问时间距离现在最近,就最不容易被删除。

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

import java.util.Map;public class LRUCache {private Map<Integer, ListNode> map;/*** 双链表结点类*/private class ListNode {private Integer key;private Integer value;/*** 前驱结点 precursor*/private ListNode pre;/*** 后继结点 successor(写成 next 是照顾单链表的表示习惯)*/private ListNode next;public ListNode() {}public ListNode(Integer key, Integer value) {this.key = key;this.value = value;}}private int capacity;/*** 虚拟头结点没有前驱*/private ListNode dummyHead;/*** 虚拟尾结点没有后继*/private ListNode dummyTail;public LRUCache(int capacity) {map = new HashMap<>(capacity);this.capacity = capacity;dummyHead = new ListNode(-1, -1);dummyTail = new ListNode(-1, -1);// 初始化链表为 head <-> taildummyHead.next = dummyTail;dummyTail.pre = dummyHead;}/*** 如果存在,把当前结点移动到双向链表的头部** @param key* @return*/public int get(int key) {if (map.containsKey(key)) {ListNode node = map.get(key);int val = node.value;// 把当前 node 移动到双向链表的头部moveNode2Head(key);return val;} else {return -1;}}/*** 如果哈希表的容量满了,就要删除一个链表末尾元素,然后在链表头部插入新元素** @param key* @param value*/public void put(int key, int value) {if (map.containsKey(key)) {// 1、更新 valuemap.get(key).value = value;// 2、把当前 node 移动到双向链表的头部moveNode2Head(key);return;}// 放元素的操作是一样的if (map.size() == capacity) {// 如果满了ListNode oldTail = removeTail();// 设计 key 就是为了在这里删除map.remove(oldTail.key);}// 然后添加元素ListNode newNode = new ListNode(key, value);map.put(key, newNode);addNode2Head(newNode);}// 为了突出主干逻辑,下面是 3 个公用的方法/*** 删除双链表尾部结点*/private ListNode removeTail() {ListNode oldTail = dummyTail.pre;ListNode newTail = oldTail.pre;// 两侧结点建立连接newTail.next = dummyTail;dummyTail.pre = newTail;// 释放引用oldTail.pre = null;oldTail.next = null;return oldTail;}/*** 把当前 key 指向的结点移到双向链表的头部** @param key*/private void moveNode2Head(int key) {// 1、先把 node 拿出来ListNode node = map.get(key);// 2、原来 node 的前驱和后继接上node.pre.next = node.next;node.next.pre = node.pre;// 3、再把 node 放在末尾addNode2Head(node);}/*** 在双链表的头部新增一个结点** @param newNode*/private void addNode2Head(ListNode newNode) {// 1、当前头结点ListNode oldHead = dummyHead.next;// 2、末尾结点的后继指向新结点oldHead.pre = newNode;// 3、设置新结点的前驱和后继newNode.pre = dummyHead;newNode.next = oldHead;// 4、更改虚拟头结点的后继结点dummyHead.next = newNode;}public static void main(String[] args) {LRUCache cache = new LRUCache(2);cache.put(1, 1);cache.put(2, 2);System.out.println(cache.map.keySet());int res1 = cache.get(1);System.out.println(res1);cache.put(3, 3);int res2 = cache.get(2);System.out.println(res2);int res3 = cache.get(3);System.out.println(res3);cache.put(4, 4);System.out.println(cache.map.keySet());int res4 = cache.get(1);System.out.println(res4);int res5 = cache.get(3);System.out.println(res5);int res6 = cache.get(4);System.out.println(res6);}
}作者:liweiwei1419
链接:https://leetcode-cn.com/problems/lru-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiw-2/

【总结】

1.缓存删除机制

  • LRU (Least Recently Used)缓存机制[第146题]
  • LFU (Least Frequently Used,最不经常使用)缓存机制[第460
    题]
  1. 时间空间tradeoff
    存取数据时间性能最好 - 哈希表
    访问某个数据,时间优先级得提前,删除末尾结点的需求,头尾访问数据最快 - 双向链表

  2. LinkedList 双向链表 本题需要定制化操作

参考链接:https://leetcode-cn.com/problems/lru-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiw-2/

[Leedcode][JAVA][第146题][LRU][哈希表][双向链表]相关推荐

  1. [Leedcode][JAVA][第105题][从前序与中序遍历序列构造二叉树][栈][递归][二叉树]

    [问题描述][中等] 根据一棵树的前序遍历与中序遍历构造二叉树.注意: 你可以假设树中没有重复的元素.例如,给出前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = ...

  2. [Leedcode][JAVA][第470题][Ran7()实现Rand10()]

    [问题描述][Leedcode][JAVA][第470题][Ran7()实现Rand10()] 已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 ...

  3. [Leedcode][JAVA][第45题][跳跃游戏 II][贪心算法]

    [问题描述][Leedcode][JAVA][第45题][跳跃游戏 II] 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2.从下标为 0 跳到下标为 1 的位置 ...

  4. [Leedcode][JAVA][第41题][缺失的第一个正数][哈希表][数组]

    [问题描述][困难] 给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数.示例 1:输入: [1,2,0] 输出: 3 示例 2:输入: [3,4,-1,1] 输出: 2 示例 3:输入: ...

  5. [Leedcode][JAVA][第560题][和为K的子数组][Hashmap][数组]

    [问题描述][第560题][和为K的子数组][中等] 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数.示例 1 :输入:nums = [1,1,1], k = 2 输 ...

  6. [Leedcode][JAVA][第974题][和可被K整除的子数组][前缀和][HashSet]

    [问题描述][中等] 给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续.非空)子数组的数目.示例:输入:A = [4,5,0,-2,-3,1], K = 5 输出:7 解释: 有 7 个子 ...

  7. [Leedcode][JAVA][第460题][LFU]

    [问题描述] 设计并实现最不经常使用(LFU)缓存的数据结构.它应该支持以下操作:get 和 put.get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1. put(ke ...

  8. [Leedcode][JAVA][第914题][最大公约数]

    [问题描述] 给定一副牌,每张牌上都写着一个整数.此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:每组都有 X 张牌. 组内所有的牌上都写着相同的整数. 仅当你可选的 ...

  9. LRU 缓存机制实现:哈希表 + 双向链表

    算法详解 LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对. 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的 ...

最新文章

  1. 数组计算的数学模块----NumPy
  2. 【ArcGIS Pro微课1000例】0010:ArcGIS Pro导入ArcMap样式符号库——以国土三调样式为例
  3. jzoj2700-数字【数论,LCM】
  4. python 编程算法_python语言编程算法
  5. Docker 操作手册
  6. Apple Watch新功能曝光:“一键收取”蚂蚁森林能量
  7. php 变量 类名,关于php:使用变量类名和静态方法时出错
  8. 贺利坚老师汇编课程53笔记:寄存器冲突问题解决方案定义子程序标准框架
  9. CART算法原理及实现
  10. 基于灰色模型GM的管道腐蚀预测 - 附代码
  11. 别再说找不到web前端项目练手了,这套最全的前端实战案例请拿去
  12. mysql交叉编译 cmake_cmake交叉编译参数toolchain
  13. 汇编程序设计与计算机体系结构软件工程师教程笔记:总结
  14. Java——字母大小写全排列
  15. 「实时视频流分析的边缘计算技术」最新2022研究综述
  16. TRUE PARTNER迎来戴维斯双击,资产规模业绩双增长
  17. DS SIMULIA Antenna Magus Professional 2021.5
  18. SFP(Soft Filter Pruning)笔记
  19. 认为的文字可编辑的 pdf和图片展示的 pdf 做笔记的方式
  20. 一年多前的Linux笔记,仅以此文纪念当时的年少无知

热门文章

  1. linux 新建用户、用户组 以及为新用户分配权限
  2. 【iOS开发每日小笔记(二)】gitHub上的开源“瀑布流”使用心得
  3. 开源路由软件zebra的命令存储原理及使用方法
  4. media recovery oracle,Oracle非归档模式Media Recovery错误之--ORA-26040
  5. jsp调用struts,jsp调用action,action获取表单提交的参数
  6. android studio 获取SHA1值 MD5值
  7. python WindroseAxes 报错 has no attribute ‘Appender‘
  8. 宝塔面板 创建 二级域名 Unable to round-trip http request to upstream
  9. python中popen的用法_python中的subprocess.Popen()使用
  10. css3修改透明png颜色