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

LeetCode 2353. 设计食物评分系统【设计,哈希表,有序集合;堆+懒删除】1781

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

设计一个支持下述操作的食物评分系统:

  • 修改 系统中列出的某种食物的评分。
  • 返回系统中某一类烹饪方式下评分最高的食物。

实现 FoodRatings 类:

  • FoodRatings(String[] foods, String[] cuisines, int[] ratings) 初始化系统。食物由 foodscuisinesratings 描述,长度均为 n
    • foods[i] 是第 i 种食物的名字。
    • cuisines[i] 是第 i 种食物的烹饪方式。
    • ratings[i] 是第 i 种食物的最初评分。
  • void changeRating(String food, int newRating) 修改名字为 food 的食物的评分。
  • String highestRated(String cuisine) 返回指定烹饪方式 cuisine 下评分最高的食物的名字。如果存在并列,返回 字典序较小 的名字。

注意,字符串 x 的字典序比字符串 y 更小的前提是:x 在字典中出现的位置在 y 之前,也就是说,要么 xy 的前缀,或者在满足 x[i] != y[i] 的第一个位置 i 处,x[i] 在字母表中出现的位置在 y[i] 之前。

示例:
输入

["FoodRatings", "highestRated", "highestRated", "changeRating", "highestRated", "changeRating", "highestRated"]
[[["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]], ["korean"], ["japanese"], ["sushi", 16], ["japanese"], ["ramen", 16], ["japanese"]]

输出

[null, "kimchi", "ramen", null, "sushi", null, "ramen"]

解释

FoodRatings foodRatings = new FoodRatings(["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]);
foodRatings.highestRated("korean"); // 返回 "kimchi"// "kimchi" 是分数最高的韩式料理,评分为 9 。
foodRatings.highestRated("japanese"); // 返回 "ramen"// "ramen" 是分数最高的日式料理,评分为 14 。
foodRatings.changeRating("sushi", 16); // "sushi" 现在评分变更为 16 。
foodRatings.highestRated("japanese"); // 返回 "sushi"// "sushi" 是分数最高的日式料理,评分为 16 。
foodRatings.changeRating("ramen", 16); // "ramen" 现在评分变更为 16 。
foodRatings.highestRated("japanese"); // 返回 "ramen"// "sushi" 和 "ramen" 的评分都是 16 。// 但是,"ramen" 的字典序比 "sushi" 更小。

提示:

  • 1 <= n <= 2 * 10^4
  • n == foods.length == cuisines.length == ratings.length
  • 1 <= foods[i].length, cuisines[i].length <= 10
  • foods[i]cuisines[i] 由小写英文字母组成
  • 1 <= ratings[i] <= 108
  • foods 中的所有字符串 互不相同
  • 在对 changeRating 的所有调用中,food 是系统中食物的名字。
  • 在对 highestRated 的所有调用中,cuisine 是系统中 至少一种 食物的烹饪方式。
  • 最多调用 changeRatinghighestRated 总计 2 * 10^4

解法1 平衡树

我们可以用一个哈希表 f s fs fs 记录每个食物名称、对应的食物评分和烹饪方式,另一个哈希表套平衡树 c s cs cs 记录每个烹饪方式、对应的食物评分和食物名称集合。

对于 changeRating 操作,先从 cs[fs[food].cuisine] 中删除旧数据,然后将 newRatingfood 记录到 c s cs cs f s fs fs 中:

// cpp
class FoodRatings {
private:unordered_map<string, pair<int, string>> fs;unordered_map<string, set<pair<int, string>>> cs;
public:FoodRatings(vector<string>& foods, vector<string>& cuisines, vector<int>& ratings) {for (int i = 0, n = foods.size(); i < n; ++i) {auto &f = foods[i], &c = cuisines[i];int r = ratings[i];fs[f] = {r, c};cs[c].emplace(-r, f);}}void changeRating(string food, int newRating) {auto &[r, c] = fs[food];auto &s = cs[c];s.erase({-r, food}); // 移除旧数据s.emplace(-newRating, food); // 添加新数据r = newRating;}string highestRated(string cuisine) {return cs[cuisine].begin()->second;}
};
// java
class FoodRatings {Map<String, Pair<Integer, String>> fs = new HashMap<>();Map<String, TreeSet<Pair<Integer, String>>> cs = new HashMap<>();public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {for (var i = 0; i < foods.length; ++i) {String f = foods[i], c = cuisines[i];var r = ratings[i];fs.put(f, new Pair<>(r, c));cs.computeIfAbsent(c, k -> new TreeSet<>((a, b) -> !Objects.equals(a.getKey(), b.getKey()) ? b.getKey() - a.getKey() : // 逆序 a.getValue().compareTo(b.getValue()))).add(new Pair<>(r, f));}}public void changeRating(String food, int newRating) {var e = fs.get(food);var s = cs.get(e.getValue());s.remove(new Pair<>(e.getKey(), food)); // 移除旧数据s.add(new Pair<>(newRating, food)); // 添加新数据fs.put(food, new Pair<>(newRating, e.getValue()));}public String highestRated(String cuisine) {return cs.get(cuisine).first().getValue();}
}
// python
from sortedcontainers import SortedSet
class FoodRatings:def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):self.fs = {}self.cs = defaultdict(SortedSet)for f, c, r in zip(foods, cuisines, ratings):self.fs[f] = [r, c]self.cs[c].add((-r, f))def changeRating(self, food: str, newRating: int) -> None:r, c = self.fs[food]s = self.cs[c]s.remove((-r, food)) # 移除旧数据s.add((-newRating, food)) # 添加新数据self.fs[food][0] = newRatingdef highestRated(self, cuisine: str) -> str:return self.cs[cuisine][0][1]

解法2 懒删除堆

另一种做法是用堆:

  • 对于 changeRating 操作,直接往 c s cs cs 中记录,不做任何删除操作;
  • 对于 h i g h e s t R a t e d highestRated highestRated 操作,查看堆顶的食物评分是否等于其实际值,若不相同则意味着对应的元素已被替换成了其他值,堆顶存的是个垃圾数据,直接弹出堆顶;否则堆顶就是答案。
// cpp
class FoodRatings {
private:unordered_map<string, pair<int, string>> fs;unordered_map<string, priority_queue<pair<int, string>, vector<pair<int, string>>, greater<>>> cs;
public:FoodRatings(vector<string>& foods, vector<string>& cuisines, vector<int>& ratings) {for (int i = 0, n = foods.size(); i < n; ++i) {auto &f = foods[i], &c = cuisines[i];int r = ratings[i];fs[f] = {r, c};cs[c].emplace(-r, f);}}void changeRating(string food, int newRating) {auto &[r, c] = fs[food];cs[c].emplace(-newRating, food); // 直接添加新数据,后面 highestRated 再删除旧的r = newRating;}string highestRated(string cuisine) {auto &q = cs[cuisine];while (-q.top().first != fs[q.top().second].first) // 堆顶的食物评分不等于其实际值q.pop();return q.top().second;}
};
// java
class FoodRatings {Map<String, Pair<Integer, String>> fs = new HashMap<>();Map<String, Queue<Pair<Integer, String>>> cs = new HashMap<>();public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {for (var i = 0; i < foods.length; ++i) {String f = foods[i], c = cuisines[i];var r = ratings[i];fs.put(f, new Pair<>(r, c));cs.computeIfAbsent(c, k -> new PriorityQueue<>((a, b) -> !Objects.equals(a.getKey(), b.getKey()) ? b.getKey() - a.getKey() : // 逆序 a.getValue().compareTo(b.getValue()))).add(new Pair<>(r, f));}}public void changeRating(String food, int newRating) {var c = fs.get(food).getValue();cs.get(c).offer(new Pair<>(newRating, food)); // 直接添加新数据,后面 highestRated 再删除旧的fs.put(food, new Pair<>(newRating, c)); // 记录新评分}public String highestRated(String cuisine) {var q = cs.get(cuisine);while (!Objects.equals(q.peek().getKey(), fs.get(q.peek().getValue()).getKey()))q.poll();return q.peek().getValue();}
}
// python
class FoodRatings:def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):self.fs = {}self.cs = defaultdict(list)for f, c, r in zip(foods, cuisines, ratings):self.fs[f] = [r, c]heappush(self.cs[c], (-r, f))def changeRating(self, food: str, newRating: int) -> None:f = self.fs[food]heappush(self.cs[f[1]], (-newRating, food)) # 直接添加新数据,后面highestRated再删除旧的f[0] = newRatingdef highestRated(self, cuisine: str) -> str:h = self.cs[cuisine]while -h[0][0] != self.fs[h[0][1]][0]: # 堆顶的食物评分!=实际值heappop(h)return h[0][1]

相关文章:

LeetCode 2353. 设计食物评分系统【设计,哈希表,有序集合;堆+懒删除】1781

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

Redis (三)

1、redis复制 简单的概括就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主&#xff0c;当master数据发生变化的时候&#xff0c;自动将更新的数据异步同步到其他的slave是数据库。 使用这种机制的话&#xff0c;可以做到读写分离&#xff0c;可以减轻主机负担…...

CompletableFuture超详解与实践

0.背景 一个接口可能需要调用 N 个其他服务的接口&#xff0c;这在项目开发中还是挺常见的。举个例子&#xff1a;用户请求获取订单信息&#xff0c;可能需要调用用户信息、商品详情、物流信息、商品推荐等接口&#xff0c;最后再汇总数据统一返回。 如果是串行&#xff08;按…...

Maven之私服

1 介绍 团队开发现状分析私服是一台独立的服务器&#xff0c;用于解决团队内部的资源共享与资源同步问题Nexus Sonatype公司的一款maven私服产品 下载地址&#xff1a;https://help.sonatype.com/repomanager3/download win版安装包&#xff1a;https://pan.baidu.com/s/1wk…...

#define宏定义的初探

前言&#xff1a; 最基本的#define定义方式 #define可以定义宏&#xff0c;这点相信大家并不陌生&#xff0c;其定义的方式十分简单&#xff0c;给大家随便来一个最简单、最基础的定义方式看看&#xff1a; #include<stdio.h> #define a 3 int main() { printf(&quo…...

机器学习 -决策树的案例

场景 我们对决策树的基本概念和算法其实已经有过了解&#xff0c;那我们如何利用决策树解决问题呢&#xff1f; 构建决策树 数据准备 我们准备了一些数据如下&#xff1a; # 定义新的数据集 new_dataSet [[晴朗, 是, 高, 是],[雨天, 否, 低, 否],[阴天, 是, 中, 是],[晴朗…...

04、Kafka ------ 各个功能的作用解释(Cluster、集群、Broker、位移主题、复制因子、领导者副本、主题)

目录 启动命令&#xff1a;CMAK的用法★ 在CMAK中添加 Cluster★ 在CMAK中查看指定集群★ 在CMAK中查看 Broker★ 位移主题★ 复制因子★ 领导者副本和追随者副本★ 查看主题 启动命令&#xff1a; 1、启动 zookeeper 服务器端 小黑窗输入命令&#xff1a; zkServer 2、启动 …...

1、C语言:数据类型/运算符与表达式

数据类型/运算符/表达式 1.数据类型与长度2.常量3.声明4. 运算符5. 表达式 1.数据类型与长度 基本数据类型 类型说明char字符型&#xff0c;占用一个字节&#xff0c;可以存放本地字符集中的一个字符int整型&#xff0c;通常反映了所有机器中整数的最自然长度float单精度浮点…...

[ffmpeg系列 03] 文件、流地址(视频)解码为YUV

一 代码 ffmpeg版本5.1.2&#xff0c;dll是&#xff1a;ffmpeg-5.1.2-full_build-shared。x64的。 文件、流地址对使用者来说是一样。 流地址(RTMP、HTTP-FLV、RTSP等)&#xff1a;信令完成后&#xff0c;才进行音视频传输。信令包括音视频格式、参数等协商。 接流的在实际…...

python算法每日一练:连续子数组的最大和

这是一道关于动态规划的算法题&#xff1a; 题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;请找出该数组中连续子数组的最大和&#xff0c;并返回这个最大和。 示例&#xff1a; 输入&#xff1a;[-2, 1, -3, 4, -1, 2, 1, -5, 4] 输出&#xff1a;6 解释&#xff…...

一个vue3的tree组件

https://download.csdn.net/download/weixin_41012767/88709466...

新手练习项目 4:简易2048游戏的实现(C++)

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#xff09; 目录 一、效果图二、代码&#xff08;带注释&#xff09;三、说明 一、效果图 二、代码&#xff08;带…...

2023年度总结:技术沉淀、持续学习

2023年度总结&#xff1a;技术沉淀、持续学习 一、引言 今年是我毕业的第二个年头&#xff0c;也是完整的一年&#xff0c;到了做年终总结的时候了 这一年谈了女朋友&#xff0c;学习了不少技术&#xff0c;是充实且美好的一年&#xff01; 首先先看年初定的小目标&#xf…...

Unity 利用UGUI之Slider制作进度条

在Unity中使用Slider和Text组件可以制作简单的进度条。 首先在场景中右键->UI->Slider&#xff0c;新建一个Slider组件&#xff1a; 同样方法新建一个Text组件&#xff0c;最终如图&#xff1a; 创建一个进度模拟脚本&#xff0c;Slider_Progressbar.cs using System.C…...

OCS2 入门教程(四)- 机器人示例

系列文章目录 前言 OCS2 包含多个机器人示例。我们在此简要讨论每个示例的主要特点。 System State Dim. Input Dim. Constrained Caching Double Integrator 2 1 No No Cartpole 4 1 Yes No Ballbot 10 3 No No Quadrotor 12 4 No No Mobile Manipul…...

FreeRTOS学习第6篇–任务状态挂起恢复删除等操作

目录 FreeRTOS学习第6篇--任务状态挂起恢复删除等操作任务的状态设计实验IRReceiver_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第6篇–任务状态挂起恢复删除等操作 本文目标&#xff1a;学习与使用FreeRTOS中的几项操作&#xff0c;有挂起恢复删除等操作…...

BLE Mesh蓝牙组网技术详细解析之Access Layer访问层(六)

目录 一、什么是BLE Mesh Access Layer访问层&#xff1f; 二、Access payload 2.1 Opcode 三、Access layer behavior 3.1 Access layer发送消息的流程 3.2 Access layer接收消息的流程 3.3 Unacknowledged and acknowledged messages 3.3.1 Unacknowledged message …...

Netlink 通信机制

文章目录 前言一、Netlink 介绍二、示例代码参考资料 前言 一、Netlink 介绍 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 在Linux 内核中&#xff0c;使用netlink 进行应用与内核通信的应用有…...

2024.1.8每日一题

LeetCode 回旋镖的数量 447. 回旋镖的数量 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定平面上 n 对 互不相同 的点 points &#xff0c;其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 &#xff0c;其中 i 和 j 之间的距离和 i 和 k 之间的欧式…...

看了致远OA的表单设计后的思考

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法

使用 ROS1-Noetic 和 mavros v1.20.1&#xff0c; 携带经纬度海拔的话题主要有三个&#xff1a; /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码&#xff0c;来分析他们的发布过程。发现前两个话题都对应了同一…...