当前位置: 首页 > news >正文

C语言 | Leetcode C语言题解之第460题LFU缓存

题目:

题解:


/*
数值链表的节点定义。
*/
typedef struct ValueListNode_s
{int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next;
}
ValueListNode;/*
计数链表的节点定义。
其中,head是数值链表的头节点,对应的是最新的数值节点。
环形链表,head->prev实际就是tail,对应的就是最久未使用的节点。
*/
typedef struct CounterListNode_s
{ValueListNode *head;struct CounterListNode_s *prev;struct CounterListNode_s *next;
}
CounterListNode;/*
对象结构定义。
capacity:           总的容量。
currentCounter:     当前已有的key的数量。
keyHash:            key的哈希数组,为空表示这个key对应数值不存在。
counterHash:        counter的哈希数组,为空表示这个counter对应的链表不存在。
head:               计数链表的头节点。
*/
typedef struct
{int capacity;int currentCounter;ValueListNode **keyHash;CounterListNode **counterHash;CounterListNode *head;
}
LFUCache;/*
几个自定义函数的声明,具体实现见下。
*/
extern void removeValueNode(CounterListNode *counterNode, ValueListNode *valueNode);
extern void insertValueNode(CounterListNode *counterNode, ValueListNode *valueNode);
extern void removeCounterNode(LFUCache *obj, CounterListNode *counterNode);
extern void insertCounterNode(LFUCache *obj, CounterListNode *counterPrev, CounterListNode *counterNode);/*
创建对象。
*/
LFUCache *lFUCacheCreate(int capacity)
{LFUCache *obj = (LFUCache *)malloc(sizeof(LFUCache));/* 总容量就等于入参capacity,当前已有的key的数量初始化为0。 */obj->capacity = capacity;obj->currentCounter = 0;/* key的取值范围是[0, 10^5],共100001个。用calloc代替malloc,即包含了初始化为空的步骤。 */obj->keyHash = (ValueListNode **)calloc(100001, sizeof(ValueListNode *));/* 题目给的操作次数上限是2*10^5。同上,用calloc代替malloc,包含了初始化为空的步骤。 */obj->counterHash = (CounterListNode **)calloc(200001, sizeof(CounterListNode *));/* 刚开始时,计数链表为空。 */obj->head = NULL;return obj;
}/*
获取指定key的数值。
value:          想要获取key对应的数值,初始化为-1,假如获取不到,就返回这个-1。
valueNode:      从keyHash中直接获取的数值链表节点。
counterNode:    在计数加一之前,这个数值节点当前所处的计数链表。
counterNew:     在计数加一之后,这个数值节点想要加入的新计数链表。
*/
int lFUCacheGet(LFUCache *obj, int key)
{int value = -1;ValueListNode *valueNode = obj->keyHash[key];CounterListNode *counterNode = NULL, *counterNew = NULL;/* 对应的key存在数值时,才需要返回其数值,否则返回-1。 */if(NULL != valueNode){/* 要返回的数值。 */value = valueNode->value;/* 这个节点当前在哪一个计数链表节点中。 */counterNode = obj->counterHash[valueNode->counter];/* 数值的计数加一。以及计数加一之后,它想要加入的新的计数链表节点。 */valueNode->counter++;counterNew = obj->counterHash[valueNode->counter];/* 把数值节点从旧的链表中移除。 */removeValueNode(counterNode, valueNode);/* 如果这个新的计数节点还不存在,则新建一个节点。 */if(NULL == counterNew){counterNew = (CounterListNode *)calloc(1, sizeof(CounterListNode));obj->counterHash[valueNode->counter] = counterNew;/* 新建计数节点,加到counterNode的后方。 */insertCounterNode(obj, counterNode, counterNew);}/* 如果旧的计数节点中的数值链表变为空,则旧的计数节点也需要从计数链表中移除。 */if(NULL == counterNode->head){removeCounterNode(obj, counterNode);free(counterNode);obj->counterHash[valueNode->counter - 1] = NULL;}/* 把数值节点加入到新的链表中。 */insertValueNode(counterNew, valueNode);}return value;
}/*
赋值指定key的数值。
keyRemove:          要被移除的键值。
valueNode:          指定的key对应的数值节点。
valueRemove:        可能被移除的数值节点。
counterNode:        在计数加一之前,这个数值节点当前所处的计数链表。
counterNew:         在计数加一之后,这个数值节点想要加入的新计数链表。
*/
void lFUCachePut(LFUCache *obj, int key, int value)
{int keyRemove = 0;ValueListNode *valueNode = obj->keyHash[key], *valueRemove = NULL;CounterListNode *counterNode = NULL, *counterNew = NULL;/* 总容量为0的话,什么都不需要做。 */if(0 == obj->capacity){return;}/* 如果这个key值已经存在,则修改其数值。 */if(NULL != valueNode){/* 修改新的数值。 */valueNode->value = value;/* 这个节点当前在哪一个计数链表节点中。 */counterNode = obj->counterHash[valueNode->counter];/* 数值的计数加一。以及计数加一之后,它想要加入的新的计数链表节点。 */valueNode->counter++;counterNew = obj->counterHash[valueNode->counter];/* 把数值节点从旧的链表中移除。 */removeValueNode(counterNode, valueNode);/* 如果这个新的计数节点还不存在,则新建一个节点。 */if(NULL == counterNew){counterNew = (CounterListNode *)calloc(1, sizeof(CounterListNode));obj->counterHash[valueNode->counter] = counterNew;/* 新建计数节点,加到counterNode的后方。 */insertCounterNode(obj, counterNode, counterNew);}/* 如果旧的计数节点中的数值链表变为空,则旧的计数节点也需要从计数链表中移除。 */if(NULL == counterNode->head){removeCounterNode(obj, counterNode);free(counterNode);obj->counterHash[valueNode->counter - 1] = NULL;}/* 把数值节点加入到新的链表中。 */insertValueNode(counterNew, valueNode);}/* 否则,新建一个键值。 */else{/* 如果没有满总量,则数量加一。 */if(obj->capacity > obj->currentCounter){obj->currentCounter++;}/* 否则,先把最近最久未使用的键移除。 */else{/* 要删除的数值节点所在的计数节点,一定是计数最少的那个counterNode,即头节点。 */counterNode = obj->head;/* 要被移除的节点,是数值链表的尾节点。 */valueRemove = counterNode->head->prev;keyRemove = valueRemove->key;/* 把它从链表中移除。 */removeValueNode(counterNode, valueRemove);/* 如果计数节点中的数值链表变成空,则也移除这个计数节点。 */if(NULL == counterNode->head){removeCounterNode(obj, counterNode);free(counterNode);obj->counterHash[valueRemove->counter] = NULL;}free(valueRemove);obj->keyHash[keyRemove] = NULL;}/* 新建一个数值节点。 */valueNode = (ValueListNode *)calloc(1, sizeof(ValueListNode));valueNode->key = key;valueNode->value = value;valueNode->counter = 1;obj->keyHash[key] = valueNode;/* 要新加入的链表。新出现的数值,计数肯定是1。 */counterNew = obj->counterHash[1];/* 如果这个计数节点还不存在,则新建一个。 */if(NULL == counterNew){counterNew = (CounterListNode *)calloc(1, sizeof(CounterListNode));obj->counterHash[1] = counterNew;/* counter为1的计数节点,肯定是加到头部的。 */insertCounterNode(obj, NULL, counterNew);}/* 把数值节点加入到新的链表中。 */insertValueNode(counterNew, valueNode);}return;
}/*
释放对象。
*/
void lFUCacheFree(LFUCache *obj)
{CounterListNode *counterNode = obj->head, *counterNext = NULL;ValueListNode *valueNode = NULL, *valueNext = NULL;/* 逐个释放计数链表的每个节点。 */while(NULL != counterNode){counterNext = counterNode->next;/* 释放每个计数链表节点下面的数值链表。环形链表的循环,使用do、while语句。 */valueNode = counterNode->head;do{valueNext = valueNode->next;free(valueNode);valueNode = valueNext;}while(counterNode->head != valueNode);free(counterNode);counterNode = counterNext;}/* 释放key的哈希数组。 */free(obj->keyHash);/* 释放counter的哈希数组。 */free(obj->counterHash);/* 释放对象。 */free(obj);return;
}/*
几个自定义函数的具体实现。
主要是双向链表、双向循环链表的节点添加、删除的操作,保证操作前后仍然是双向链表、双向循环链表。
*//*
把数值节点从数值链表中删除。
*/
void removeValueNode(CounterListNode *counterNode, ValueListNode *valueNode)
{/* 如果这个被删除节点是链表中的唯一一个,则删除之后直接为空链表。 */if(valueNode->next == valueNode){counterNode->head = NULL;}/* 否则把它的前后两个节点连接起来。 */else{valueNode->prev->next = valueNode->next;valueNode->next->prev = valueNode->prev;/* 如果删掉的就是头节点,则新的头节点的位置往后挪一位。 */if(counterNode->head == valueNode){counterNode->head = valueNode->next;}}return;
}/*
把数值节点加入到数值链表头部。
*/
void insertValueNode(CounterListNode *counterNode, ValueListNode *valueNode)
{ValueListNode *tail = NULL;/* 如果本身是空链表,则它是其中唯一节点。 */if(NULL == counterNode->head){valueNode->prev = valueNode;valueNode->next = valueNode;}/* 否则就把它插入到原来的头尾之间。 */else{tail = counterNode->head->prev;valueNode->prev = tail;valueNode->next = counterNode->head;counterNode->head->prev = valueNode;tail->next = valueNode;}/* 它成为新的头节点。 */counterNode->head = valueNode;return;
}/*
把计数节点从计数链表中删除。
*/
void removeCounterNode(LFUCache *obj, CounterListNode *counterNode)
{/* 如果删除的本身是头节点,则头节点将变为下一个。 */if(obj->head == counterNode){obj->head = counterNode->next;if(NULL != obj->head){obj->head->prev = NULL;}}/* 否则,把它的前后两个节点连起来。不是头节点的话,prev肯定存在,next可能为空。 */else{counterNode->prev->next = counterNode->next;if(NULL != counterNode->next){counterNode->next->prev = counterNode->prev;}}return;
}/*
把一个新的计数节点加入到计数链表指定节点counterPrev的后方。
如果counterPrev为空,则表示加到链表头。
*/
void insertCounterNode(LFUCache *obj, CounterListNode *counterPrev, CounterListNode *counterNode)
{/* 如果counterPrev为空,说明是加入到头节点的位置。 */if(NULL == counterPrev){counterNode->prev = NULL;counterNode->next = obj->head;if(NULL != obj->head){obj->head->prev = counterNode;}obj->head = counterNode;}/* 否则插入到counterPrev和counterPrev->next之间。 */else{counterNode->prev = counterPrev;counterNode->next = counterPrev->next;if(NULL != counterPrev->next){counterPrev->next->prev = counterNode;}counterPrev->next = counterNode;}return;
}

相关文章:

C语言 | Leetcode C语言题解之第460题LFU缓存

题目: 题解: /* 数值链表的节点定义。 */ typedef struct ValueListNode_s {int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next; } ValueListNode;/* 计数链表的节点定义。 其中,head是数值链表的头…...

【AI论文精读12】RAG论文综述2(微软亚研院 2409)P4-隐性事实查询L2

AI知识点总结:【AI知识点】 AI论文精读、项目、思考:【AI修炼之路】 P1,P2,P3 四、隐性事实查询(L2) 4.1 概述 ps:P2有四种查询(L1,L2,L3,L4&…...

SpringBoot中间件Docker

Docker(属于C/S架构软件) 简介与概述 1.Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux …...

计算机毕设选题推荐【大数据专业】

计算机毕设选题推荐【大数据专业】 大数据专业的毕业设计需要结合数据的采集、存储、处理与分析等方面的技能。为帮助同学们找到一个适合且具有实践性的选题,我们为大家整理了50个精选的毕设选题。这些选题涵盖了大数据分析、处理技术、可视化等多个方向&#xff0…...

Bootstrap 4 多媒体对象

Bootstrap 4 多媒体对象 引言 Bootstrap 4 是目前最受欢迎的前端框架之一,它提供了一套丰富的工具和组件,帮助开发者快速构建响应式和移动设备优先的网页。在本文中,我们将重点探讨 Bootstrap 4 中的多媒体对象(Media Object)组件,这是一种用于构建复杂和灵活布局的强大…...

Springmvc Thymeleaf 标签

Thymeleaf是一个适用于Java的模板引擎,它允许开发者将动态内容嵌入到HTML页面中。在SpringMVC框架中,Thymeleaf可以作为一个视图解析器,使得开发者能够轻松地创建动态网页。以下是关于SpringMVC中Thymeleaf标签的详细介绍: 一、T…...

用java来编写web界面

一、ssm框架整体目录架构 二、编写后端代码 1、编写实体层代码 实体层代码就是你的对象 entity package com.cv.entity;public class Apple {private Integer id;private String name;private Integer quantity;private Integer price;private Integer categoryId;public…...

如何利用Fiddler进行抓包并自动化

首先一般使用Fiddler都是对手机模拟器进行抓包 接下来以MUMU模拟器为例 首先打开Fiddler-->tool-->options-->connection 将要打上的勾都打上,可以看到代理的端口是8888 打开HTTPS选项 把要打的勾打上,这样子才可以接收到HTTPS的包 MUMU打开…...

权重衰减与暂退法——pytorch与paddle实现模型正则化

权重衰减与暂退法——pytorch与paddle实现模型正则化 在深度学习中,模型正则化是一种至关重要的技术,它有助于防止模型过拟合,提高泛化能力。过拟合是指在训练数据上表现良好,但在测试数据或新数据上表现不佳的现象。为了缓解这一…...

MYSQL-windows安装配置两个或多个版本MYSQL

安装第一个mysql很简单,这里不再赘述。主要说说第二个怎么安装,服务怎么配置。 1. 从官网下载第二个MySQL并安装 一般都是免安装版了,下载解压到某个文件目录下(路径中尽量不要带空格或中文),再新建一个my.ini文件(或…...

6、Spring Boot 3.x集成RabbitMQ动态交换机、队列

一、前言 本篇主要是围绕着 Spring Boot 3.x 与 RabbitMQ 的动态配置集成,比如动态新增 RabbitMQ 交换机、队列等操作。二、默认RabbitMQ中的exchange、queue动态新增及监听 1、新增RabbitMQ配置 RabbitMQConfig.java import org.springframework.amqp.rabbit.a…...

【分布式微服务云原生】 探索SOAP协议:简单对象访问协议的深度解析与实践

探索SOAP协议:简单对象访问协议的深度解析与实践 摘要: 在现代分布式系统中,SOAP(简单对象访问协议)扮演着至关重要的角色,提供了一种标准化的方式来实现不同系统间的通信。本文深入探讨了SOAP的工作原理、…...

C语言题目练习2

前面我们知道了单链表的结构及其一些数据操作,今天我们来看看有关于单链表的题目~ 移除链表元素 移除链表元素: https://leetcode.cn/problems/remove-linked-list-elements/description/ 这个题目要求我们删除链表中是指定数据的结点,最终返…...

复变函数与积分变换——留数定理求拉氏逆变换

1.留数定理 若s1,s2,…,sn是F(s)的所有奇点(函数在某个点上的取值无定义或者无限大),且当s→∞时,F(s)→0,则有: 一般地: s1是一级极点,则&#…...

RabbitMQ事务模块

目录 消息分发​​​​​​​ 负载均衡 幂等性保障 顺序性保障 顺序性保障方案 二号策略:分区消费 三号策略:消息确认机制 四号策略: 消息积压 RabbitMQ集群 选举过程 RabbitMQ是基于AMQP协议实现的,该协议实现了事务机制,要么全部成功,要么全…...

Android终端GB28181音视频实时回传设计探讨

技术背景 好多开发者,在调研Android平台GB28181实时回传的时候,对这块整体的流程,没有个整体的了解,本文以大牛直播SDK的SmartGBD设计开发为例,聊下如何在Android终端实现GB28181音视频数据实时回传。 技术实现 Andr…...

AI金融攻防赛:金融场景凭证篡改检测(DataWhale组队学习)

引言 大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年10月学习赛的AI金融攻防赛学习总结文档。本文主要讲解如何解决 金融场景凭证篡改检测的核心问题,以及解决思路和代码实现过程。希望…...

华为OD机试真题---喊7的次数重排

题目描述 喊7是一个传统的聚会游戏。N个人围成一圈,按顺时针从1到N编号。编号为1的人从1开始喊数,下一个人喊的数字为上一个人的数字加1。但是,当将要喊出来的数字是7的倍数或者数字本身含有7时,不能把这个数字直接喊出来&#x…...

使用阿里巴巴的图

参考链接1 引用彩色图标可参考以下链接 &#xff08;到第三步 测试图标效果 的时候 还是可以保持之前的写法&#xff1a;<i/sapn class“iconfont icon-xxx”>也会出现彩色的&#xff09; 参考链接2 阿里巴巴字体使用 也可以直接将官网的代码复制过来到页面的css区域...

【hot100-java】排序链表

链表题。 使用归并排序法。 一图解决。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; thi…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...