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]

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

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

相关文章

map升序和降序

1.升序排序&#xff08;TreeMap&#xff08;默认是升序排列的&#xff09;&#xff09; Map<String, Integer> map new HashMap<>(); // 使用Stream API对Map进行升序排序 Map<String, Integer> sortedMap new LinkedHashMap<>(); map.entrySet().st…

【力扣每日一题】力扣447回旋镖的数量

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

路由器02_静态路由DHCP

一、静态路由 &#xff11;、静态路由特点 由管理员手工配置&#xff0c;是单向的&#xff0c;缺乏灵活性 &#xff12;、默认路由 默认路由是一种比较特殊静态路由&#xff0c;一般用于末节&#xff08;末梢&#xff09;网络&#xff0c;直接指定目标为任何地方 二、静态…

Flutter中showModalBottomSheet的属性介绍和使用

在Flutter中&#xff0c;showModalBottomSheet是一个常用的工具&#xff0c;用于在屏幕底部显示模态底部面板。了解其属性将帮助您更好地定制和控制底部模态框的外观和行为。 showModalBottomSheet的常用属性 1. context: 类型: BuildContext描述: 表示当前构建上下文&#…

为什么 Kafka 这么快?它是如何工作的?

随着数据以指数级的速度流入企业&#xff0c;强大且高性能的消息传递系统至关重要。Apache Kafka 因其速度和可扩展性而成为热门选择&#xff0c;但究竟是什么让它如此之快&#xff1f; 在本期中&#xff0c;我们将探讨&#xff1a; Kafka 的架构及其核心组件&#xff0c;如生…

Xfs文件系统磁盘布局

目录 一&#xff0c;CentOS下Xfs文件系统的安装 二&#xff0c;准备工作 三&#xff0c;AG结构 四&#xff0c;AG超级块 五&#xff0c;AG空闲磁盘空间管理 六&#xff0c;ABTB的Btree 七&#xff0c;ABTB/ABTC的节点块管理 八&#xff0c;inode节点管理 九&#xff0…

【力扣100】34.在排序数组中查找元素的第一个和最后一个位置 || 时间复杂度大小

添加链接描述 class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:nlen(nums)i,j0,n-1if not n:return [-1,-1]while i<j:midi(j-i)//2if target<nums[mid]:jmid-1elif target>nums[mid]:imid 1else:x,ymid,midwhile x-1>0 and…

Vue-5、el和data的两种写法

1、el 第一种写法 <!DOCTYPE html> <html lang"en" xmlns:v-model"http://www.w3.org/1999/xhtml" xmlns:v-bind"http://www.w3.org/1999/xhtml"> <head><meta charset"UTF-8"><title>el和data的两种写…

vue3中路由的使用(详细讲解)

1、路由的简介 路由(route)&#xff1a;就是根据特定的规则将数据包或请求从源地址传输到目标地址的过程。 在前端或者vue3项目中路由主要用于构建单页面应用程序&#xff08;SPA&#xff09;&#xff0c;其中所有的页面都在同一个HTML文件中加载&#xff0c;通过JavaScript动…

【git使用】常用的 git 撤销操作(restore、reset、revert)

常用命令总结 注意坑 新增文件是未被跟踪状态&#xff08;untracked&#xff09;&#xff0c;也就是下面命令不会对此文件生效所以新增文件&#xff0c;需要先被 git 记录到&#xff0c;也就是 git add new-file之后执行这些操作&#xff0c;就会生效了 # 丢弃工作区的修改&…

自监督深度学习技术

一、定义 自监督学习&#xff08;SSL&#xff09;是机器学习的一种范式&#xff0c;用于处理未标记数据以获取有用的表示&#xff0c;以帮助下游学习任务。SSL方法最显著的特点是它们不需要人类标注的标签&#xff0c;这意味着它的训练完全基于由未标记的数据样本组成的数据集…

网络通信过程的一些基础问题

客户端A在和服务器进行TCP/IP通信时&#xff0c;发送和接收数据使用的是同一个端口吗&#xff1f; 这个问题可以这样来思考&#xff1a;在客户端A与服务器B建立连接时&#xff0c;A需要指定一个端口a向服务器发送数据。当服务器接收到A的报文时&#xff0c;从报文头部解析出A的…

04.循环结构

循环结构 应用场景 我们在写程序的时候&#xff0c;一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球&#xff0c;如果机器人持球而且还没有进入射门范围&#xff0c;那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中&#xff0c;让…

018、通用集合类型

Rust标准库包含了一系列非常有用的被称为集合的数据结构。大部分的数据结构都代表着某个特定的值&#xff0c;但集合却可以包含多个值。 与内置的数组与元组类型不同&#xff0c;这些集合将自己持有的数据存储在了堆上。这意味着数据的大小不需要在编译时确定&#xff0c;并且可…

matlab subs 函数计算太慢

来源 计算机器人 transformation matrix 相关内容时&#xff0c;对于关节角度进行离散&#xff0c;循环计算很慢&#xff0c;随着角度划分越来越细&#xff0c;怎么提高速度是一个问题。 最优解决方法 fun_handle matlabFunction(T_t2b_RPY_tmp);T_t2b_RPY_tmp是 transform…

WEB 3D技术 three.js 顶点交换

本文 我们来说 顶点的转换 其实就是 我们所有顶点的位置发生转变 我们整个物体的位置也会随之转变 这里 我们编写代码如下 import ./style.css import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.j…

kettle的基本介绍和使用

1、 kettle概述 1.1 什么是kettle Kettle是一款开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装&#xff0c;数据抽取高效稳定。 1.2 Kettle核心知识点 1.2.1 Kettle工程存储方式 以XML形式存储以资源库方式存储…

【数据结构】树的遍历

树的遍历 前序遍历 前序遍历是按照根节点->左子树->右子树的顺序进行遍历 图片来源维基百科深度优先遍历&#xff08;前序遍历&#xff09;: F, B, A, D, C, E, G, I, H. 代码实现 递归 # class TreeNode: # def __init__(self, x): # self.val x # …

Office提示内存或磁盘空间不足

Office提示内存或磁盘空间不足 Office提示内存或磁盘空间不足&#xff0c;可以试试以下方法&#xff0c;不管用不要骂我 打开选项 点击信任中心 在受保护的视图中将以下选项取消勾选后确定&#xff0c;关掉软件重新打开

Java实现责任链模式

责任链模式是一种设计模式&#xff0c;用于处理请求的解耦。在责任链模式中&#xff0c;多个对象都有机会处理请求&#xff0c;从而避免了请求发送者和接收者之间的直接依赖关系。每个处理者都可以决定是否处理请求以及将请求传递给下一个处理者。 简介 责任链模式由一条链组…