当前位置: 首页 > 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…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…...

MLP实战二:MLP 实现图像数字多分类

任务 实战&#xff08;二&#xff09;&#xff1a;MLP 实现图像多分类 基于 mnist 数据集&#xff0c;建立 mlp 模型&#xff0c;实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入&#xff0c;可视化图形数字&#xff1b; 2、完成数据预处理&#xff1a;图像数据维度转换与…...

21-Oracle 23 ai-Automatic SQL Plan Management(SPM)

小伙伴们&#xff0c;有没有迁移数据库完毕后或是突然某一天在同一个实例上同样的SQL&#xff0c; 性能不一样了、业务反馈卡顿、业务超时等各种匪夷所思的现状。 于是SPM定位开始&#xff0c;OCM考试中SPM必考。 其他的AWR、ASH、SQLHC、SQLT、SQL profile等换作下一个话题…...

【Elasticsearch基础】Elasticsearch批量操作(Bulk API)深度解析与实践指南

目录 1 Bulk API概述 1.1 什么是批量操作 1.2 Bulk API的优势 2 Bulk API的工作原理 2.1 请求处理流程 2.2 底层机制 3 Bulk API的使用方法 3.1 基本请求格式 3.2 操作类型示例 3.3 响应格式 4 Bulk API的最佳实践 4.1 批量大小优化 4.2 错误处理策略 4.3 性能调…...