好久没有记录了,今天做一道题,想到了拓扑排序,但是苦于学习的时候没有真正实现过拓扑排序,所以在代码实现的过程中遇到了重重阻碍,不得已去学习题解的写法,倒也是有了深刻的认知。
文章中引用了别人讲解unordered_map的CSDN博客,因为觉得写的很好所以贴上链接不迷路
先上代码:
class Solution
{
public:
vector<string> findAllRecipes(vector<string>& recipes, vector<vector<string>>& ingredients, vector<string>& supplies)
{
int n = recipes.size();//菜肴数
// 图
unordered_map<string, vector<string>> depend;//这是一个无序关联式容器
//对于它的用法,这里贴上别人写的博客,方便自己查找学习,讲这个容器的
// https://blog.csdn.net/zou_albert/article/details/106983268
// 入度统计
unordered_map<string, int> cnt;
//拓扑排序的精髓就是如果一个结点的入度为0,那么它就不依赖任何其他结点
//本题中就是意味着要么原材料,要么是可以做出来的菜
for (int i = 0; i < n; ++i)
{
//遍历某个菜的菜谱
for (const string& ing: ingredients[i])
{
//注意这里是把原材料作为关键字key,后面的value是一个vector,存储依赖这个原材料的菜
depend[ing].push_back(recipes[i]);
}
//这些个要做的菜的入度,也就是依赖的原材料个数就是菜谱中元素的个数
cnt[recipes[i]] = ingredients[i].size();
}
vector<string> ans;
queue<string> q;
// 把初始的原材料放入队列
for (const string& sup: supplies)
{
q.push(sup);
}
// 拓扑排序
while (!q.empty())
{
string cur = q.front();
//这里可以理解为有很多个盘子,一个盘子对应想做的菜。往下就开始操作了
//最开始这里只有初始的原材料,它开始遍历,挨个看谁需要我这种原材料,需要就往盘子里放,放一个这道菜就不再需要这种原材料了,那么它的cnt,也就是入度就减一
q.pop();
if (depend.count(cur)) //由于不存在相同的key,所以这实际在检查图中是否有这个结点
{
//也就是看一看你们剩的这些菜还需不需要我这个原材料
for (const string& rec: depend[cur])
{
//这块就是操作的主要部分了
--cnt[rec];
// 如果入度变为 0,说明可以做出这道菜
if (cnt[rec] == 0)
{
ans.push_back(rec);//太棒了,可以做
q.push(rec);//变成原材料了,放进原材料队列
}
}
}
}
return ans;
}//函数体
};
这道题对于深刻理解拓扑排序有很好的帮助,精刷一道题约等与随便看看十道题,我觉得我理解了,过段时间再重新自己码一次,建议和我一样对拓扑排序的实现不太熟悉的好兄弟可以看看这道题,如果精力充足的话可以趁热打铁再来几道。
今天就写到这里吧,又学会了一个知识点嘿嘿