LFU算法

LFU算法

Least Frequently Used(最不频繁使用)
在这里插入图片描述
Leetcode有原题,之前手写过LRU,数据结构还是习惯于用java实现,实现是copy的评论题解。
题解注释写的很清楚

大致就是说LFUCache类维护一个存放node的map,同时维护两个双向链表,注意这个双向链表里面又包含了两个双向链表,访问的频率是first最大,last最小。其余的就是正常的双向链表的操作了(插入,删除)

import java.util.HashMap;
import java.util.Map;

class LFUCache {

    Map<Integer, Node> cache; // 存储缓存的内容,Node中除了value值外,还有key、freq、所在doublyLinkedList、所在doublyLinkedList中的postNode、所在doublyLinkedList中的preNode,具体定义在下方。

    DoublyLinkedList firstLinkedList; // firstLinkedList.post 是频次最大的双向链表

    DoublyLinkedList lastLinkedList; // lastLinkedList.pre 是频次最小的双向链表,满了之后删除 lastLinkedList.pre.tail.pre
                                     // 这个Node即为频次最小且访问最早的Node

    int size;

    int capacity;

    public LFUCache(int capacity) {

        cache = new HashMap<>(capacity);

        firstLinkedList = new DoublyLinkedList();

        lastLinkedList = new DoublyLinkedList();

        firstLinkedList.post = lastLinkedList;  // 是按照倒序排列的,最大的是first

        lastLinkedList.pre = firstLinkedList;

        this.capacity = capacity;

    }

    public int get(int key) {

        Node node = cache.get(key);

        if (node == null) {

            return -1;

        }

        // 该key访问频次+1

        freqInc(node);

        return node.value;

    }

    public void put(int key, int value) {

        if (capacity == 0) {

            return;

        }

        Node node = cache.get(key);

        // 若key存在,则更新value,访问频次+1

        if (node != null) {

            node.value = value;

            freqInc(node);

        } else {

            // 若key不存在

            if (size == capacity) {

                // 如果缓存满了,删除lastLinkedList.pre这个链表(即表示最小频次的链表)中的tail.pre这个Node(即最小频次链表中最先访问的Node),如果该链表中的元素删空了,则删掉该链表。

                cache.remove(lastLinkedList.pre.tail.pre.key);

                lastLinkedList.removeNode(lastLinkedList.pre.tail.pre);

                size--;

                if (lastLinkedList.pre.head.post == lastLinkedList.pre.tail) {

                    removeDoublyLinkedList(lastLinkedList.pre);

                }

            }

            // cache中put新Key-Node对儿,并将新node加入表示freq为1的DoublyLinkedList中,若不存在freq为1的DoublyLinkedList则新建。

            Node newNode = new Node(key, value);

            cache.put(key, newNode);

            if (lastLinkedList.pre.freq != 1) {

                DoublyLinkedList newDoublyLinedList = new DoublyLinkedList(1);

                addDoublyLinkedList(newDoublyLinedList, lastLinkedList.pre);

                newDoublyLinedList.addNode(newNode);

            } else {

                lastLinkedList.pre.addNode(newNode);

            }

            size++;

        }

    }

    /**
     * node的访问频次 + 1
     */
    void freqInc(Node node) {

        // 将node从原freq对应的双向链表里移除, 如果链表空了则删除链表。

        DoublyLinkedList linkedList = node.doublyLinkedList;

        DoublyLinkedList preLinkedList = linkedList.pre;

        linkedList.removeNode(node);

        if (linkedList.head.post == linkedList.tail) {

            removeDoublyLinkedList(linkedList);

        }

        // 将node加入新freq对应的双向链表,若该链表不存在,则先创建该链表。

        node.freq++;

        if (preLinkedList.freq != node.freq) {

            DoublyLinkedList newDoublyLinedList = new DoublyLinkedList(node.freq);

            addDoublyLinkedList(newDoublyLinedList, preLinkedList);

            newDoublyLinedList.addNode(node);

        } else {

            preLinkedList.addNode(node);

        }

    }

    /**
     * 增加代表某1频次的双向链表
     */
    void addDoublyLinkedList(DoublyLinkedList newDoublyLinedList, DoublyLinkedList preLinkedList) {

        newDoublyLinedList.post = preLinkedList.post;

        newDoublyLinedList.post.pre = newDoublyLinedList;

        newDoublyLinedList.pre = preLinkedList;

        preLinkedList.post = newDoublyLinedList;

    }

    /**
     * 删除代表某1频次的双向链表
     */
    void removeDoublyLinkedList(DoublyLinkedList doublyLinkedList) {

        doublyLinkedList.pre.post = doublyLinkedList.post;

        doublyLinkedList.post.pre = doublyLinkedList.pre;

    }

}

class Node {

    int key;

    int value;

    int freq = 1;

    Node pre; // Node所在频次的双向链表的前继Node

    Node post; // Node所在频次的双向链表的后继Node

    DoublyLinkedList doublyLinkedList; // Node所在频次的双向链表

    public Node() {
    }

    public Node(int key, int value) {

        this.key = key;

        this.value = value;

    }

}

class DoublyLinkedList {

    int freq; // 该双向链表表示的频次

    DoublyLinkedList pre; // 该双向链表的前继链表(pre.freq < this.freq)

    DoublyLinkedList post; // 该双向链表的后继链表 (post.freq > this.freq)

    Node head; // 该双向链表的头节点,新节点从头部加入,表示最近访问

    Node tail; // 该双向链表的尾节点,删除节点从尾部删除,表示最久访问

    public DoublyLinkedList() {

        head = new Node();

        tail = new Node();

        head.post = tail;

        tail.pre = head;

    }

    public DoublyLinkedList(int freq) {

        head = new Node();

        tail = new Node();

        head.post = tail;

        tail.pre = head;

        this.freq = freq;

    }

    void removeNode(Node node) {

        node.pre.post = node.post;

        node.post.pre = node.pre;

    }

    void addNode(Node node) {

        node.post = head.post;

        head.post.pre = node;

        head.post = node;

        node.pre = head;
        node.doublyLinkedList = this;

    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

立创EDA学习:设计收尾工作

布线整理 ShiftM&#xff0c;关闭铺铜显示 调整结束后再使用快捷键”ShiftM“打开铺铜 过孔 在空白区域加上一些GND过孔&#xff0c;连接顶层与底层的铺铜。放置好”过孔“后&#xff0c;隐藏铺铜&#xff0c;观察刚才放置的过孔有没有妨碍到其他器件 调整铺铜 先打开铺铜区&…

php mysql字段默认值使用问题

前提是使用了事务&#xff0c;在第一个阶段 是A表操作保存&#xff0c;第二阶段操作B表&#xff0c;操作B表的时候使用了A表的一个字段&#xff0c;这个字段在第一阶段没有设置值&#xff0c;保存的时候使用字段默认值。 【这种情况 最好是在第一阶段 把后面要使用的字段设置好…

C#在图片上输出文字和保存

winform&#xff0c;图片控件&#xff0c;加载一个图片&#xff0c;在图片上输出文字&#xff1b; 输出文字的代码如下&#xff1b; private void pictureBox1_Paint(object sender, PaintEventArgs e){Graphics g1 e.Graphics;g1.DrawString("测试", this.Font, B…

物联网IOT视频设备如何快速对接阿里云生活物联网(Link Visual)并成功上云?

原文永久更新地址&#xff1a;https://www.yundashi168.com/472.html 文章来源&#xff1a;猿视野 如果有图片看不清楚&#xff0c;加载不出来&#xff0c;请阅读原文。 什么是Link Visual、 Link Visual是生活物联网平台针对视频产品推出的增值服务&#xff0c;提供视频数据上…

指针的深入理解1

1.如何理解指针 假设有一栋宿舍楼&#xff0c;把你放在楼里&#xff0c;楼上有100个房间&#xff0c;但是房间没有编号&#xff0c;你的一个朋友来找你玩&#xff0c; 如果想找到你&#xff0c;就得挨个房子去找&#xff0c;这样效率很低&#xff0c;但是我们如果根据楼层和楼…

C#,恩廷格尔组合数(Entringer Number)的算法与源程序

恩廷格尔组合数&#xff08;Entringer Number&#xff09;组合数学的序列数字之一。 E&#xff08;n&#xff0c;k&#xff09;是{1&#xff0c;2&#xff0c;…&#xff0c;n1}的排列数&#xff0c;从k1开始&#xff0c;先下降后上升。 计算结果&#xff1a; 源代码&#xf…

用Excel辅助做数独

做数独游戏的时候&#xff0c;画在纸上很容易弄花眼&#xff0c;所以我考虑用Excel辅助做一个。 界面如下&#xff1a; 按下初始化表格区域按钮&#xff0c;会在所有单元格中填充“123456789”。如下图&#xff1a; 当某个单元格删除得只剩一个数字时&#xff0c;会将同一行、…

[UI5 常用控件] 03.Icon, Avatar,Image

文章目录 前言1. Icon2. Avatar2.1 displayShape2.2 initials2.3 backgroundColor2.4 Size2.5 fallbackIcon2.6 badgeIcon2.7 badgeValueState2.8 active 3. Image 前言 本章节记录常用控件Title,Link,Label。 其路径分别是&#xff1a; sap.m.Iconsap.m.Avatarsap.m.Image 1…

C++面试:散列表

目录 1. 散列表的基本概念 散列表的定义 散列函数 哈希冲突 2. 处理冲突的方法 链地址法&#xff08;Separate Chaining&#xff09; 开放地址法 再散列 3. 散列表的性能分析 1. 平均查找长度&#xff08;ASL&#xff09; 2. 负载因子&#xff08;Load Factor&#…

CAD-autolisp(二)——选择集、命令行设置对话框、符号表

目录 一、选择集1.1 选择集的创建1.2 选择集的编辑1.3 操作选择集 二、命令行设置对话框2.1 设置图层2.2 加载线型2.3 设置字体样式2.4 设置标注样式&#xff08;了解即可&#xff09; 三、符号表3.1 简介3.2 符号表查找3.2 符号表删改增 一、选择集 定义&#xff1a;批量选择…

go语言(十八)---- goroutine

一、goroutine package mainimport ("fmt""time" )func main() {//用go创建承载一个形参为空&#xff0c;返回值为空的一个函数go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")//退出当前goroutinefmt…

《WebKit 技术内幕》学习之十五(6):Web前端的未来

6 Chromium OS和Chrome的Web应用 6.1 基本原理 HTML5技术已经不仅仅用来编写网页了&#xff0c;也可以用来实现Web应用。传统的操作系统支持本地应用&#xff0c;那么是否可以有专门的操作系统来支持Web应用呢&#xff1f;当然&#xff0c;现在已经有众多基于Web的操作系统&…

跟无神学AI之Prompt

在大模型时代会写prompt变得很重要。 Prompt翻译为中文为提示词&#xff0c;在大模型的特定领域指的是大模型使用者给大模型提交的一种有一定格式的交互命令&#xff0c;让我们看看科大讯飞的大模型给出的答案—— Prompt是一种向人工智能模型提供的输入文本或指令&#xff0…

uv胶UV大灯修复液修复汽车车灯大灯尾灯?

使用UV胶进行汽车大灯修复是一种常见的方法&#xff0c;特别是用于修复裂纹、划痕或氧化的透明塑料表面。以下是使用UV胶修复汽车大灯的一般步骤&#xff1a; 1.准备工作&#xff1a; 确保汽车大灯表面是干净的&#xff0c;没有灰尘、油脂或其他污垢。可以使用清洁剂和软布进行…

Microsoft Remote Desktop for Mac(远程桌面连接)激活版

Microsoft Remote Desktop是一款由微软开发的远程桌面连接工具&#xff0c;它允许用户从另一台计算机或移动设备远程连接到Windows桌面或服务器。 以下是该软件的一些主要特点和功能&#xff1a; 跨平台支持&#xff1a;Microsoft Remote Desktop支持Windows、macOS、iOS和Andr…

leetcode:二叉树的中序遍历(外加先序,后序遍历)

题外&#xff1a;另外三种遍历可以看这&#xff1a; 层序遍历&#xff1a; Leetcode:二分搜索树层次遍历-CSDN博客 先序遍历&#xff1a; 二叉树的先序&#xff0c;中序&#xff0c;后序遍历-CSDN博客 后序遍历&#xff1a; 二叉树的先序&#xff0c;中序&#xff0c;后序…

网络安全全栈培训笔记(58-服务攻防-应用协议设备KibanaZabbix远控向日葵VNCTV)

第58天 服务攻防-应用协议&设备Kibana&Zabbix&远控向日葵&VNC&TV 知识点&#xff1a; 1、远程控制第三方应用安全 2、三方应用-向日葵&VNC&TV 3、设备平台-Zabbix&Kibanai漏洞 章节内容&#xff1a; 常见版务应用的安全测试&#xff1a; 1…

【Web】CTFSHOW SQL注入刷题记录(上)

目录 无过滤注入 web171 web172 web173 web174 web175 时间盲注 写马 过滤注入 web176 web177 web178 web179 web180 web181-182 web183 web184 web185-186 web187 web188 web189 web190 布尔盲注 web191 web192 web193 web194 堆叠注入 web195 …

[C++]使用纯opencv部署yolov8旋转框目标检测

【官方框架地址】 https://github.com/ultralytics/ultralytics 【算法介绍】 YOLOv8是一种先进的对象检测算法&#xff0c;它通过单个神经网络实现了快速的物体检测。其中&#xff0c;旋转框检测是YOLOv8的一项重要特性&#xff0c;它可以有效地检测出不同方向和角度的物体。…

格子表单GRID-FORM | 嵌套子表单与自定义脚本交互

格子表单/GRID-FORM已在Github 开源&#xff0c;如能帮到您麻烦给个星&#x1f91d; GRID-FORM 系列文章 基于 VUE3 可视化低代码表单设计器嵌套表单与自定义脚本交互 新版本功能 &#x1f389; 不觉间&#xff0c;GRID-FORM 已经开源一年&#xff08;2023年1月29日首次提交…