【每日刷题】Day157
🥕个人主页:开敲🍉
🔥所属专栏:每日刷题🍍
🌼文章目录🌼
[1. 207. 课程表 - 力扣(LeetCode)](#1. 207. 课程表 - 力扣(LeetCode))
[2. LCR 113. 课程表Ⅱ - 力扣(LeetCode)](#2. LCR 113. 课程表Ⅱ - 力扣(LeetCode))
[3. LCR 114. 火星词典 - 力扣(LeetCode)](#3. LCR 114. 火星词典 - 力扣(LeetCode))
1. 207. 课程表 - 力扣(LeetCode)
//思路:BFS+拓扑排序
//拓扑排序:针对有向无环图的排序算法。
//BFS实现拓扑排序:将有向无环图中所有 入度 为0的点入队列,随后向外扩展,每次取出队列头部点,并将连接该点的边删去,并将与该点相连的点的入度--:
//当最后图中没有任何点 || 没有任何入度为 0 的点时,说明可以将所有课程学完,返回true;否则,返回 false
class Solution {
public:
bool canFinish(int n, vector<vector<int>>& p)
{
unordered_map<int,vector<int>> hash;
vector<int> edges(n);//记录每个点的入度
for(auto& e : p)//建图
{
int a = e[0],b = e[1];
hash[b].push_back(a);
edges[a]++;
}
queue<int> qu;
for(int i = 0;i<n;i++)//将所有入度为 0 的点入队列
if(!edges[i]) qu.push(i);
while(!qu.empty())
{
int tmp = qu.front();
qu.pop();
for(int i = 0;i<hash[tmp].size();i++)
if(!(--edges[hash[tmp][i]])) qu.push(hash[tmp][i]);//每次取出队列头部点,并将连接该点的边删去,并将与该点相连的点的入度--,如果元素的入度减为 0 ,则继续入队列
}
for(int i = 0;i<n;i++)
if(edges[i]) return false;//如果最后图中还有点,则返回 false
return true;
}
};
2. LCR 113. 课程表Ⅱ - 力扣(LeetCode)
//思路:BFS+拓扑排序。
//思路与上一题 "课程表" 完全一样,代码也几乎一模一样,只需要将每次入度为 0 的点放入一个结果数组中即可。
class Solution {
public:
vector<int> findOrder(int n, vector<vector<int>>& p)
{
vector<int> ans;
vector<int> tmp;
unordered_map<int,vector<int>> hash;
vector<int> edges(n);
for(auto& e : p)//建图
{
int a = e[0],b = e[1];
hash[b].push_back(a);
edges[a]++;
}
queue<int> qu;
for(int i = 0;i<n;i++)//将所有入度为 0 的点放入队列和结果数组中。
{
if(!edges[i])
{
qu.push(i);
ans.push_back(i);
}
}
while(!qu.empty())
{
int tmp = qu.front();
qu.pop();
for(int i = 0;i<hash[tmp].size();i++)//每次取出队列头部点,并将连接该点的边删去,并将与该点相连的点的入度--,如果元素的入度减为 0 ,则继续入队列,并且将其放入结果数组中。
{
if(!(--edges[hash[tmp][i]]))
{
qu.push(hash[tmp][i]);
ans.push_back(hash[tmp][i]);
}
}
}
for(int i = 0;i<n;i++)//如果无法完成所有课程,则返回空数组
if(edges[i]) return tmp;
return ans;
}
};
3. LCR 114. 火星词典 - 力扣(LeetCode)
//思路:BFS+拓扑排序+哈希 。
//本题的难点在于:如何记录字符的边以及入度个数?
//这里是用哈希表的方式解决的:
① 记录边:使用哈希嵌套哈希的方式,对于每个字符出去的边,记录在一个 unordered_set中,这样可以避免重复记录,如 "wrt" "er" "ett",如果不使用哈希表记录会重复记录 w -> e这条边
② 记录入度个数:使用哈希表,对于每个 char,记录有多少个入度。使用时必须先初始化------ 将 words 中出现的所有字符的入度初始化为 0
class Solution
{
unordered_map<char,unordered_set<char>> edges;//记录边
unordered_map<char,int> in;//记录入度个数
bool check = false;//这个用于处理特殊情况:"abc" "ab",这种情况下是不合法的,但是我们的拓扑排序没法解决
public:
void add(string& s1,string& s2)//建图
{
int sub = 0;
while(sub<min(s1.size(),s2.size()))
{
int a = s1[sub],b = s2[sub];
if(a!=b) //遇到不同字符时记录
{
if(!edges[a].count(b))//建边前判断一下是否已经存在了这条边,防止重复记录,如上面的 w -> e
{
edges[a].insert(b);//建边
in[b]++;//记录入度个数
}
return;
}
sub++;
}
if(s1.size()>s2.size())//当前 min(s1.size(),s2.size())个字符都相同时就会跳出上面循环来到这,如果前一个字符串的长度 > 后一个字符串的长度,则不合法,将 check 置为 true
check = true;
}
string alienOrder(vector<string>& words)
{
string ans;
int n = words.size();
for(auto& s : words)
for(auto c : s)
in[c] = 0;
for(int i = 0;i<n;i++)
{
for(int j = i+1;j<n;j++)
{
add(words[i],words[j]);//两层 for 循环建图
if(check) return "";//当 check 为 true时,返回空串
}
}
queue<char> qu;
for(auto& [a,b] : in)//将所有入度为 0 的字符入队列
if(!b) qu.push(a);
while(!qu.empty())//BFS
{
char tmp = qu.front();
qu.pop();
ans+=tmp;
for(char c : edges[tmp])
if(!(--in[c])) qu.push(c);//每次取出队列头部点,并将连接该点的边删去,并将与该点相连的点的入度--,如果元素的入度减为 0 ,则继续入队列,并且将其放入结果数组中。
}
for(auto& [a,b] : in)//最后判断图中是否还存在点
if(b) return "";
return ans;
}