题目大致意思是在一个有向有环图中,若从某个节点开始是无环的状态(以某个节点为起点是有向无环图),那么记录这个节点为安全节点,最后返回安全节点的集合。
我刚拿到题,就从正向开始暴力dfs搜索,顺便用一个circle记录不安全节点,若探索到的节点位于不安全节点集合中,那么在探索的点也必定是不安全点。
但这个思路不可避免要走很多遍graph数组,几乎四O(n2)的,于是超时了。。。
官方题解:拓扑排序,时间复杂O(n+m)
首先介绍拓扑排序,这是对于有向无环图定义的一种排序,有点类似BFS(需要用到队列),但也可以用来判断图中是否存在环。因为根据宫水三叶大神的解析,无环图必然存在入度为0和出度为0的点,否则,每个节点都有前驱or后继节点,那么经过n+1条边,必然经过重复节点,即成环。
拓扑排序一定要从读入为0的节点开始
步骤:
- 将所有入度为0的元素加入队列
- 弹出队列首元素,遍历所有它出度的节点,将到达的节点入度减一(将现在正在处理的节点从图中剔除)
- 若刚才减一操作出现入度为0的情况,push入队列
- 循环2.3.步骤,直到队列为空(遍历一遍所有路径)
若是有环图,因为环中元素入度永远不为0,或者说没有办法由已知入度为0的节点逐步剔除,导致他们的入度也为0,没有剔除他们的方法,遍历过后,入度不为0的就是环中元素了
根据题意,要寻找安全节点,安全路径上一定存在出度为0的节点,由前面拓扑排序的方法,可以先构造返图,再由(原来出度=0)现在入度=0的点出发,进行拓扑排序,找到最后入度为0的点,就是安全节点。
代码
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
//返图
vector<int> ans;
vector<vector<int>> rg(graph.size());
vector<int> innode(graph.size(), 0);
for (int i = 0; i < graph.size(); i++){
for (auto p : graph[i]){
rg[p].push_back(i);
}
innode[i] = graph[i].size();//源图的出度,反图的入度
}
//
queue<int> q;
for (int i = 0; i < innode.size(); i++){
//
if (innode[i] == 0){
cout << "i " << i << endl;
q.push(i);
}
}
cout << q.size();
//
while(!q.empty()){
//
int node = q.front();
q.pop();
for (auto path : rg[node]){
//
if (--innode[path] == 0){
q.push(path);
}
}
}
//innode == 0的无环
for (int i = 0; i < innode.size(); i++){
if (innode[i] == 0){
ans.push_back(i);
}
}
return ans;
}
};