https://leetcode.cn/problems/design-a-food-rating-system
- 问题描述(这部分可以直接复制)
设计一个支持下述操作的食物评分系统:
修改系统中列出的某种食物的评分。
返回系统中某一类烹饪方式下评分最高的食物。
实现 `FoodRatings` 类:
FoodRatings(String[] foods, String[] cuisines, int[] ratings)初始化系统。食物由 `foods`、`cuisines` 和 `ratings` 描述,长度均为 `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` 之前,也就是说,要么 `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] <= 10^8
foods 中的所有字符串互不相同
在对 `changeRating` 的所有调用中,`food` 是系统中食物的名字。
在对 `highestRated` 的所有调用中,`cuisine` 是系统中至少一种食物的烹饪方式。
最多调用 `changeRating` 和 `highestRated` 总计 2 * 10^4 次
- 解题思路
使用unordered_map 和 set`来存储食物评分和烹饪方式的信息,来实现了初始化系统、修改评分和返回最高评分食物的功能。
构造函数FoodRatings,在初始化系统时,遍历食物数组,将食物名称、评分和烹饪方式存储到 `fs` 和 `cs` 中。在 cs 中,使用 multiset`存储食物评分和名称的对应关系,按照评分的负值进行排序。
使用changeRating方法:根据食物名称找到对应的评分和烹饪方式,然后在对应的 multiset 中更新评分信息。首先从cs中移除旧评分信息,然后插入新的评分信息,并更新fs中的评分值。
最后使用highestRated` 方法:根据指定的烹饪方式,在对应的multiset 中找到评分最高的食物名称并返回。
- 算法描述(应当有明确具体的步骤,可以使用代码加注释、伪代码、纯文字描述均可)
class FoodRatings {
//记录食物名称对应的食物评分和烹饪方式
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 循环遍历foods数组
for (int i = 0; i < foods.size(); ++i) {
//将数组中的元素foods[i]和cuisines[i]分别赋值给f和c
auto &f = foods[i], &c = cuisines[i];
//将评分数组ratings中的第i个元素赋值给变量r,表示当前食物的评分
int r = ratings[i];
//将食物f作为键,将评分r和烹饪方式c组成的pair作为值,存储到fs中
fs[f] = {r, c};
//将烹饪方式c作为键,将一个pair(包含-r 和 f)插入到对应键的set中
//这里使用emplace函数,按照评分的负值进行排序
cs[c].emplace(-r, f);
}
}
void changeRating(string food, int newRating) {
//从fs中根据食物名称food查找对应的评分r和烹饪方式c,
auto &[r, c] = fs[food];//通过引用的方式,可以直接修改原始数据
//根据烹饪方式c,从cs中获取对应的multiset s,用于存储该烹饪方式下的食物信息
auto &s = cs[c];
//在set s中移除评分为-r(负数表示原评分的负值)且食物为food的元素
s.erase({-r, food});
//向set s 中添加一个新的元素,该元素的评分为-newRating(newRating 的负值)且食物为food
s.emplace(-newRating, food);
//将评分r更新为新的评分newRating
r = newRating;
}
string highestRated(string cuisine) {
//通过查找对应烹饪方式的multiset中的第一个元素(评分最高的食物),可以快速找到评分最高的食物名称并返回
return cs[cuisine].begin()->second;//按负值排序则begin()返回的就是最高的值
}
};
- 力扣提交结果
- 分析、讨论与总结
在实现这个类的过程中,我使用了unordered_map<string, pair<int, string>> fs来存储食物名称对应的评分和烹饪方式,以及unordered_map<string, set<pair<int, string>>> cs来存储烹饪方式对应的食物评分和食物名字集合,能够快速查找和修改对应信息。
其中使用multiset存储食物评分和名称的对应关系,按照评分的负值进行排序,可以快速找到评分最高的食物,这可以使得算法更加高效。
收获是通过使用合适的数据结构和算法,可以实现高效的评分查找和更新功能。