下面先给出该题目的简单介绍,偷个懒就不打字了,直接截图
下面是实现的代码:
class FM{
public:
string food,cuisine;
int rate;
FM(){
food = cuisine = "";
rate = 0;
}
FM(string food,string cuisine,int rate):food(food),cuisine(cuisine),rate(rate){
//cout<<"普通构造函数被调用了"<<endl;
}
FM(const FM& f):food(f.food),cuisine(f.cuisine),rate(f.rate){
//cout<<"拷贝构造函数被调用了"<<endl;
}
bool operator<(const FM &other) const
{
if (rate == other.rate)
return food > other.food;
return rate < other.rate;
}
};
class FoodRatings {
unordered_map<string, FM> fs;
unordered_map<string, set<FM>> cs;
public:
FoodRatings(vector<string> &foods, vector<string> &cuisines, vector<int> &ratings) {
for (int i = 0; i < foods.size(); ++i) {
string f = foods[i], c = cuisines[i];
int r = ratings[i];
FM fm(f,c,r);
fs[f] = fm;
cs[c].emplace(fm);
}
}
void changeRating(string food, int newRating) {
string c = fs[food].cuisine;
cs[c].erase(fs[food]);
fs[food].rate = newRating;
cs[c].emplace(fs[food]);
}
string highestRated(string cuisine) {
return cs[cuisine].rbegin()->food;
}
};
这个题目不难,中等水平,不过却包含了非常多的知识点
一. set与unordered_set的存储区别
set的底层实现是平衡二叉树,而unordered_set是哈希表,这就要求unordered_set的内容必须是可哈希的,而set则不需要;set是有序的,unordered_set是无序的;set搜索元素的复杂度是对数级别,而unordered_set是线性级别。
二. 如何利用set对自定义类型进行排序
set中的元素自动排序,这就要求不能直接改变元素的内容,因为这样会破坏排好的序,必须先删除再插入;set有内置的排序函数,但是对于自定义类型,则需要先对操作符进行重载,当然也有自定义比较函数的,不过我认为重载操作符比较直观。
class FM{
public:
string food,cuisine;
int rate;
FM(){
food = cuisine = "";
rate = 0;
}
FM(string food,string cuisine,int rate):food(food),cuisine(cuisine),rate(rate){
//cout<<"普通构造函数被调用了"<<endl;
}
FM(const FM& f):food(f.food),cuisine(f.cuisine),rate(f.rate){
//cout<<"拷贝构造函数被调用了"<<endl;
}
bool operator<(const FM &other) const
{
if (rate == other.rate)
return food > other.food;
return rate < other.rate;
}
};
重载操作符,参数必须为常量引用,否则会报错。另外自定义数据类型要实现普通构造函数和拷贝构造函数,拷贝构造函数的参数也必须是常量引用,可以使用初始化列表进行初始化。
三. 什么情况下会调用拷贝构造函数
1.显式地以一个类对象作为另一个类对象的初值
2.当类对象被作为参数交给函数时
3.当函数返回一个类对象时
对于本题
fs[f] = fm;//没有调用拷贝构造函数
cs[c].insert(fm);//调用了拷贝构造函数
set调用insert语句时调用了FM类的拷贝构造函数,哈希表fs没有调用任何构造函数,只是做了关联。
四. begin()和end()
对于set而言,我们可以用begin()或rbegin()来取得排序的第一个元素和最后一个元素,但不能使用end(),因为end()指向的是最后一个元素的下一个元素,其实也就是空。
平时写文章比较少,知识层次也有限,如有不妥欢迎大家批评指正,我们共同进步。
下面把使用优先队列(默认也就是大顶堆)的解法也贴出来,优先队列的自定义排序
class FM{
public:
string food,cuisine;
int rate;
FM(){
food = cuisine = "";
rate = 0;
}
FM(string food,string cuisine,int rate):food(food),cuisine(cuisine),rate(rate){
//cout<<"普通构造函数被调用了"<<endl;
}
FM(const FM& f):food(f.food),cuisine(f.cuisine),rate(f.rate){
//cout<<"拷贝构造函数被调用了"<<endl;
}
bool operator<(const FM &other) const
{
if (rate == other.rate)
return food > other.food;
return rate < other.rate;
}
};
class FoodRatings {
unordered_map<string, FM> fs;
unordered_map<string, priority_queue<FM>> cs;
public:
FoodRatings(vector<string> &foods, vector<string> &cuisines, vector<int> &ratings) {
for (int i = 0; i < foods.size(); ++i) {
string f = foods[i], c = cuisines[i];
int r = ratings[i];
FM fm(f,c,r);
fs[f] = fm;
cs[c].push(fm);
}
}
void changeRating(string food, int newRating) {
string c = fs[food].cuisine;
fs[food].rate = newRating;
cs[c].push(fs[food]);// 直接添加新数据,后面 highestRated 再删除旧的
}
string highestRated(string cuisine) {
while(true){
string f = cs[cuisine].top().food;
if(fs[f].rate!=cs[cuisine].top().rate) cs[cuisine].pop();
else break;
}
return cs[cuisine].top().food;
}
};
还是使用了和set方案一样的自定义类FM,因为类里面已经重载了<操作符,所以直接一行代码就能实现初始化:
priority_queue<FM>
大顶堆对应的排序规则就是<或者仿函数less<>,如果是小顶堆则时>或仿函数greater<>,大顶堆就是队头元素最大。
除了重载操作符,也可以使用仿函数来实现,不过太麻烦,还是重载操作符来的直观。
不过,仿函数也有它的应用场合,比如下面我实现的哈夫曼树
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct TreeNode{
int val;//权值
int depth;//深度
TreeNode* left,*right;
TreeNode(int val):val(val),depth(0),left(nullptr),right(nullptr){}
TreeNode(const TreeNode& that):val(that.val),depth(that.depth),left(that.left),right(that.right){}
};
class Cmp {
public:
bool operator()(const TreeNode* a, const TreeNode* b) {
return a->val>b->val;
}
};
int getTotalWeight(TreeNode* root){
int sum = 0;
if(root->left==nullptr&&root->right==nullptr) return root->depth*root->val;//带权路径
if(root->left!=nullptr){
root->left->depth = root->depth+1;
sum+=getTotalWeight(root->left);
}
if(root->right!=nullptr){
root->right->depth = root->depth+1;
sum+=getTotalWeight(root->right);
}
return sum;
}
int main()
{
vector<int> weight = {5,29,7,8,14,23,3,11};
priority_queue<TreeNode*,vector<TreeNode*>,Cmp> q;
for(auto&w:weight) q.push(new TreeNode(w));
while(q.size()>1){
TreeNode* T1 = q.top();q.pop();
TreeNode* T2 = q.top();q.pop();
TreeNode* Root = new TreeNode(T1->val+T2->val);
Root->left = T1;
Root->right = T2;
q.push(Root);
}
TreeNode* root = q.top();q.pop();
int sum = getTotalWeight(root);
cout<<"这棵树的带权路径和为:"<<sum<<endl;
return 0;
}
关于set自定义排序方面,有一个绝佳应用,要不是碰到题目真心想不到,按对角线遍历,力扣题目1424
一开始我的想法是把矩阵补全,然后正常遍历,不过给的测试案例太变态,超时了,后来看到解题思路里面,有人提到了对角线上的元素i+j是相等的,基于此想到了构建一个struct对象
struct Point{
int dragon;
int j;
int value;
Point(int dragon,int j,int value):dragon(dragon),j(j),value(value){}
bool operator<(const Point &other) const
{
if (dragon == other.dragon)
return j < other.j;
return dragon < other.dragon;
}
};
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& nums) {
//缺列不缺行,补齐就行
set<Point> st;
for(int i=0;i<nums.size();i++){
for(int j=0;j<nums[i].size();j++){
st.insert(Point(i+j,j,nums[i][j]));
}
}
vector<int> res;
for(auto&s:st) res.push_back(s.value);
return res;
}
};
为了描述完整性,把之前矩阵补齐的代码也贴出来,说实话要不是测试案例太变态,不至于超时,也算一个不错的思路:
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& nums) {
//缺列不缺行,补齐就行
int M = nums.size();
int N = 0;
for(auto&r:nums) N = max(N,(int)r.size());
for(auto&r:nums) r.resize(N,0);
vector<int> res;
int i=0,j=0,c=0,row = 0,col = 1;
bool rowFirst = true;
while(c++<M*N){
if(nums[i][j]!=0) res.push_back(nums[i][j]);
if((i>0)&&(j<N-1)) {i-=1;j+=1;continue;}
if(rowFirst){
if((i==0)||(j==N-1))
{
i=++row;j=0;
if(i==M){
i = M-1;j=1;rowFirst = false;
}
}//顶到边界了
}
else{
if((j==N-1)||(i==0))
{
j=++col;i=M-1;
if(j==N){
break;
}
}//顶到边界了
}
}
return res;
}
};
再补充一个优先队列使用lambda表达式自定义排序的例子
题目来自于力扣1696、跳跃游戏
class Solution {
public:
int maxResult(vector<int>& nums, int k) {
int n = nums.size();
vector<int> dp(n,INT_MIN);
dp[0] = nums[0];
auto cmp = [&](int i, int j) -> bool
{
return dp[i] < dp[j];
};
priority_queue<int,vector<int>,decltype(cmp)> q(cmp);
q.push(0);
for(int i=1;i<n;i++){
while(q.top()<i-k) q.pop();
dp[i] = dp[q.top()]+nums[i];
q.push(i);
}
return dp[n-1];
}
};