题目大意:从一个结点出发,最终能走到一个没有出边的点,那么这个起始点称为安全点。给出有向图的邻接表,问图里哪些点是安全点。
分析:
方法一:dfs。这道题的意思就是看从哪些点出发没有能回到原点的环存在。给图中的结点上色,初始化为0号色(color[i]=0表示没访问过该结点),dfs时遍历到的结点染1号色(表示访问过该节点),遍历邻接表判断如果无环则该节点染2号色(表示安全无环)。
方法二:拓扑排序。安全节点=无出边。将图中的所有边反向(注意题中给的graph是邻接表,不是leetcode210中给出的边集合),此时安全节点=无入边的点(入度为0),与安全节点相连的点也是安全节点,所以将入度为0的点和它的出边删掉后,剩下的入度为0的点也是安全节点,这一过程就是拓扑排序的过程。
代码:
方法一:dfs
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<int> color(n);
vector<int> ans;
for(int i = 0;i < n;i++){ //dfs每个结点判断是否有环
if(dfs(i,color,graph))
ans.push_back(i);
}
return ans;
}
bool dfs(int i,vector<int>& color,vector<vector<int>>& graph){
if(color[i]) return color[i] == 2; //如果染色是安全状态2就返回true
color[i] = 1; //初始染1号色,相当于标记这次dfs过程中访问过它,但它的状态不确定是否安全
for(auto nei : graph[i]){
// 邻接节点不安全 || 邻接节点的邻接节点不安全
if(color[nei] == 1 || !dfs(nei,color,graph))
return false;
}
color[i] = 2;
return true;
}
};
方法二:拓扑排序
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
vector<vector<int>> pre(graph.size()); //记录每个节点的后续结点
vector<int> degree(graph.size(),0); //记录每个结点的入度
for (int i = 0;i < graph.size();i++) {
for(int j = 0;j < graph[i].size();j++){
degree[i]++;
pre[graph[i][j]].push_back(i);
}
}
queue<int> q; //队列维护入度为0的结点
for(int i = 0;i < graph.size();i++){
if(degree[i] == 0) q.push(i);
}
vector<int> schedule;
while(!q.empty()){
int now = q.front();
q.pop();
schedule.push_back(now);
for(int i = 0;i < pre[now].size();i++){
degree[pre[now][i]]--;
if(degree[pre[now][i]] == 0) q.push(pre[now][i]);
}
}
sort(schedule.begin(),schedule.end());
return schedule;
}
};