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

【问题描述】

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。进阶:
你是否可以在 O(1) 时间复杂度内执行两项操作?示例:LFUCache cache = new LFUCache( 2 /* capacity (缓存容量) */ );cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回 1
cache.put(3, 3);    // 去除 key 2
cache.get(2);       // 返回 -1 (未找到key 2)
cache.get(3);       // 返回 3
cache.put(4, 4);    // 去除 key 1
cache.get(1);       // 返回 -1 (未找到 key 1)
cache.get(3);       // 返回 3
cache.get(4);       // 返回 4来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lfu-cache

【解答思路】

1. 核心思想:先考虑访问次数,在访问次数相同的情况下,再考虑缓存的时间


  • 每次访问一个已经存在的元素的时候:应该先把结点类从当前所属的访问次数双链表里删除,然后再添加到它「下一个访问次数」的双向链表的头部。
    weiwei

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

import java.util.HashMap;
import java.util.Map;public class LFUCache {/*** key 就是题目中的 key* value 是结点类*/private Map<Integer, ListNode> map;/*** 访问次数哈希表,使用 ListNode[] 也可以,不过要占用很多空间*/private Map<Integer, DoubleLinkedList> frequentMap;/*** 外部传入的容量大小*/private Integer capacity;/*** 全局最高访问次数,删除最少使用访问次数的结点时会用到(这个设计可能是冗余的)*/private Integer maxFrequent = 1;public LFUCache(int capacity) {map = new HashMap<>(capacity);frequentMap = new HashMap<>();this.capacity = capacity;}/*** get 一次操作,访问次数就增加 1;* 从原来的链表调整到访问次数更高的链表的表头** @param key* @return*/public int get(int key) {// 测试测出来的,capacity 可能传 0if (capacity == 0) {return -1;}if (map.containsKey(key)) {// 获得结点类ListNode listNode = removeListNode(key);// 挂接到新的访问次数的双向链表的头部int frequent = listNode.frequent;addListNode2Head(frequent, listNode);return listNode.value;} else {return -1;}}/*** @param key* @param value*/public void put(int key, int value) {// 如果 key 存在,就更新访问次数 + 1,更新值if (map.containsKey(key)) {ListNode listNode = removeListNode(key);// 更新 valuelistNode.value = value;int frequent = listNode.frequent;addListNode2Head(frequent, listNode);return;}// 如果 key 不存在// 1、如果满了,先删除访问次数最小的的末尾结点,再删除 map 里对应的 keyif (map.size() == capacity) {for (int i = 1; i <= maxFrequent; i++) {if (frequentMap.containsKey(i) && frequentMap.get(i).count > 0) {// 1、从双链表里删除结点DoubleLinkedList doubleLinkedList = frequentMap.get(i);ListNode removeNode = doubleLinkedList.removeTail();// 2、删除 map 里对应的 keymap.remove(removeNode.key);break;}}}// 2、再创建新结点放在访问次数为 1 的双向链表的前面ListNode newListNode = new ListNode(key, value);addListNode2Head(1, newListNode);map.put(key, newListNode);}// 以下部分主要是结点类和双向链表的操作/*** 结点类,是双向链表的组成部分*/private class ListNode {private int key;private int value;private int frequent = 1;private ListNode pre;private ListNode next;public ListNode() {}public ListNode(int key, int value) {this.key = key;this.value = value;}}/*** 双向链表*/private class DoubleLinkedList {/*** 虚拟头结点,它无前驱结点*/private ListNode dummyHead;/*** 虚拟尾结点,它无后继结点*/private ListNode dummyTail;/*** 当前双向链表的有效结点数*/private int count;public DoubleLinkedList() {this.dummyHead = new ListNode(-1, -1);this.dummyTail = new ListNode(-1, -1);dummyHead.next = dummyTail;dummyTail.pre = dummyHead;count = 0;}/*** 把一个结点类添加到双向链表的开头(头部是最新使用数据)** @param addNode*/public void addNode2Head(ListNode addNode) {ListNode oldHead = dummyHead.next;// 两侧结点指向它dummyHead.next = addNode;oldHead.pre = addNode;// 它的前驱和后继指向两侧结点addNode.pre = dummyHead;addNode.next = oldHead;count++;}/*** 把双向链表的末尾结点删除(尾部是最旧的数据,在缓存满的时候淘汰)** @return*/public ListNode removeTail() {ListNode oldTail = dummyTail.pre;ListNode newTail = oldTail.pre;// 两侧结点建立连接newTail.next = dummyTail;dummyTail.pre = newTail;// 它的两个属性切断连接oldTail.pre = null;oldTail.next = null;count--;return oldTail;}}/*** 将原来访问次数的结点,从双向链表里脱离出来** @param key* @return*/private ListNode removeListNode(int key) {// 获得结点类ListNode deleteNode = map.get(key);ListNode preNode = deleteNode.pre;ListNode nextNode = deleteNode.next;// 两侧结点建立连接preNode.next = nextNode;nextNode.pre = preNode;// 删除去原来两侧结点的连接deleteNode.pre = null;deleteNode.next = null;// 维护双链表结点数frequentMap.get(deleteNode.frequent).count--;// 访问次数加 1deleteNode.frequent++;maxFrequent = Math.max(maxFrequent, deleteNode.frequent);return deleteNode;}/*** 把结点放在对应访问次数的双向链表的头部** @param frequent* @param addNode*/private void addListNode2Head(int frequent, ListNode addNode) {DoubleLinkedList doubleLinkedList;// 如果不存在,就初始化if (frequentMap.containsKey(frequent)) {doubleLinkedList = frequentMap.get(frequent);} else {doubleLinkedList = new DoubleLinkedList();}// 添加到 DoubleLinkedList 的表头doubleLinkedList.addNode2Head(addNode);frequentMap.put(frequent, doubleLinkedList);}
}作者:liweiwei1419
链接:https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiwei/

测试用例

public class LFUCache {public static void main(String[] args) {LFUCache cache = new LFUCache(3);cache.put(1, 1);cache.put(2, 2);cache.put(3, 3);System.out.println(cache.map.keySet());cache.put(4, 4);System.out.println(cache.map.keySet());int res1 = cache.get(4);System.out.println(res1);int res2 = cache.get(3);System.out.println(res2);int res3 = cache.get(2);System.out.println(res3);int res4 = cache.get(1);System.out.println(res4);cache.put(5, 5);int res5 = cache.get(1);System.out.println(res5);int res6 = cache.get(2);System.out.println(res6);int res7 = cache.get(3);System.out.println(res7);int res8 = cache.get(4);System.out.println(res8);int res9 = cache.get(5);System.out.println(res9);}
}

【总结】

1.LFU (Least Frequently Used)缓存机制(看访问次数)
-在缓存满的时候,删除缓存里使用次数最少的元素,然后在缓存中放入新元素;
-数据的访问次数很重要,访问次数越多,就越不容易被删除;
-根据题意,「当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除」,即在「访问次数」相同的情况下,按照时间顺序,先删除在缓存里时间最久的数据

2.编码总结
image.png

3.功力仍未够深厚 只能理解其思想

文章转载链接: https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiwei/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/426045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java学习(143):file方法类实现

import java.io.File; import java.io.IOException; import java.net.URI; import java.util.List;//文件管理类 public class FileManager {public static File createFileAction(URI uri){//使用URI做出参数创建对象if(uri!null)return new File( uri );return null;}//dir文…

性能测试十九:jmeter参数优化+排错

一&#xff1a;参数优化 1&#xff0c;控制台取样间隔的设置&#xff0c;在jmeter/bin/jmeter.properties文件中修改 summariser.interval10&#xff0c;默认为30s&#xff0c;最低可修改为6s 2&#xff0c;Jvm参数优化 bin目录下&#xff0c;vi jmeter&#xff0c;修改HEAP的…

pythonxml模块高级用法_Python利用ElementTree模块处理XML的方法详解

前言最近因为工作的需要&#xff0c;在使用 Python 来发送 SOAP 请求以测试 Web Service 的性能&#xff0c;由于 SOAP 是基于 XML 的&#xff0c;故免不了需要使用 python 来处理 XML 数据。在对比了几种方案后&#xff0c;最后选定使用 xml.etree.ElementTree模块来实现。这篇…

java学习(144):file常用方法1

import java.io.File; import java.io.IOException; import java.net.URI; import java.util.List;//文件管理类 public class FileManager {public static File createFileAction(URI uri){//使用URI做出参数创建对象if(uri!null)return new File( uri );return null;}//dir文…

[Leedcode][JAVA][第72题][动态规划]

【问题描述】 [72. 编辑距离] 给你两个单词 word1 和 word2&#xff0c;请你计算出将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作&#xff1a;插入一个字符 删除一个字符 替换一个字符示例 1&#xff1a;输入&#xff1a;word1 "horse&…

Docker操作笔记(二)容器

容器 一、启动容器 启动一个容器有两种方式&#xff1a; 1.基于镜像新键并启动一个容器&#xff1a; 所需要的主要命令为docker run docker run ubuntu:18.04 /bin/echo "hello" #启动一个bash终端 docker run -t -i ubuntu:18.04 /bin/bash 其中&#xff0c;-t 选项…

java学习(145):file常用方法2

import java.io.File; import java.io.IOException; import java.net.URI; import java.util.List;//文件管理类 public class FileManager {public static File createFileAction(URI uri){//使用URI做出参数创建对象if(uri!null)return new File( uri );return null;}//dir文…

java uipath_10.3 UiPath如何调用Java

调用Java方法(Invoke Java Method)的介绍从Java Scope中的.jar加载的方法中调用指定的Java方法。并结果存储在变量中二、Invoke Java Method 在UiPath中的使用打开设计器, 在设计库中新建一个Sequence&#xff0c;为序列命名及设置Sequence存放的路径, 在Activities中搜索Java …

Mybatis注解开发之@Results

写在前面&#xff1a;在使用mybatis注解开发的时候&#xff0c;数据库返回的结果集和实体类字段不对应&#xff0c;我们就需要手动指定映射关系&#xff1b; 一种是使用在xml文件中指定resultMap&#xff0c;指定id&#xff0c;下面需要的直接引用id就可以&#xff1b; 另一种在…

java学习(146):file常用方法3

import java.io.File; import java.io.IOException; import java.net.URI; import java.util.List;//文件管理类 public class FileManager {public static File createFileAction(URI uri){//使用URI做出参数创建对象if(uri!null)return new File( uri );return null;}//dir文…

[算法][二分查找][排除法]

最基本的二分查找算法 「搜索区间」是 [left, right] nums[mid] target 时可以立即返回 int binary_search(int[] nums, int target) {int left 0, right nums.length - 1; while(left < right) {int mid left (right - left) / 2;//防止大数溢出if (nums[mid] < t…

php getcount_PHP中关键字interface和implements详解

搜索热词PHP 类是单继承&#xff0c;也就是不支持多继承&#xff0c;当一个类需要多个类的功能时&#xff0c;继承就无能为力了&#xff0c;为此 PHP 引入了类的接口技术。如果一个抽象类里面的所有方法都是抽象方法&#xff0c;且没有声明变量&#xff0c;而且接口里面所有的成…

python操作文件

一、python中对文件、文件夹操作时经常用到的os模块和shutil模块常用方法。1.得到当前工作目录&#xff0c;即当前Python脚本工作的目录路径: os.getcwd()2.返回指定目录下的所有文件和目录名:os.listdir()3.函数用来删除一个文件:os.remove()4.删除多个目录&#xff1a;os.rem…

java学习(147):字节输入流

//读取二进制文件&#xff0c;字节输入流 import java.io.*;public class FileMana {public static void readBitFile(String filename){//1建立目标要读取的文件对象File filenew File( filename );//2基于目标对象建立输入流InputStream innull;if(file.exists()){//如果文件…

[Leedcode][JAVA][面试题 01.07][找规律][旋转数组]

【问题描述】 [面试题 01.07. 旋转矩阵] 示例 1:给定 matrix [[1,2,3],[4,5,6],[7,8,9] ],原地旋转输入矩阵&#xff0c;使其变为: [[7,4,1],[8,5,2],[9,6,3] ]给你一幅由 N N 矩阵表示的图像&#xff0c;其中每个像素的大小为 4 字节。请你设计一种算法&#xff0c;将图像…

php pmp是什么,pmp 总时差 自由时差 说明 和计算

pmp 总时差 自由时差 说明 和计算总浮动时间LS-ES 或 LF-EF总浮动时间是针对同一个活动来说的自由浮动时间是针对两个紧邻活动来说的自由活动时间紧后活动的最早开始时间-紧前活动的最晚结束时间总时差同一个活动的LS-ESLS:最晚开始&#xff0c;ES&#xff1a;最早开始eg:(以下…

java学习(148):三个参数的输入流

//3个参数读取二进制文件 import java.io.*;public class FileManage {public static void readBitFile(String filename){//1建立目标要读取的文件对象File filenew File( filename );//2基于目标对象建立输入流InputStream innull;if(file.exists()){//如果文件存在&#xff…

[剑指offer][JAVA][面试题第13题][机器人的运动][DFS][BFS]

【问题描述】 [中等] 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&#xff0c;也不能进入行坐标和列坐标的数位之和…

ios php rsa,RSA 加密 iOS

在iOS端使用RSA加密的记录一、需求&#xff1a;SDK开发&#xff0c;使用RSA加密和后台进行数据交互&#xff0c;后台是PHP要求&#xff1a;1、post请求&#xff0c;客户端放公钥&#xff0c;私钥放后台2、发送数据&#xff1a;客户端的json数据—>RSA加密数据—>base64编…

java学习(149):字符输入流

import javax.imageio.IIOException; import java.io.*; import java.util.Date;//字符输入流 public class FileManagerChar {public static void readCharFile(File file){FileReader fileReadernull;//文本输入流if(file.exists()){try {fileReader new FileReader( file )…