判断有向图是否有环,拓扑排序
1.BFS(kahn算法)
参考:
https://leetcode.com/problems/course-schedule/discuss/58665/My-C%2B%2B-solution-23ms-beats-100-submissions
按照邻接表的格式保存图,同时计算每个节点的入度并保存。设置一个队列保存入度为0的点。每次取队头节点出队列,并将与其相邻的其他节点的入度减1,如果入度变为0则入队列。循环这一步骤。
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {//prerequisites的每个元素其实代表一条边
vector<vector<int>> graph(numCourses); //保存图,下标为每个节点,对应的vector为以该节点为首的其他节点
vector<int> indegree(numCourses,0); //保存每个节点的入度,下标为节点,值为入度
for(auto edge:prerequisites)
{
graph[edge[1]].push_back(edge[0]);//edge[1]为首节点,比如(1,0)表示0指向1,0为首节点
indegree[edge[0]]++; //1的入度加1
}
queue<int> zero_q; //保存入度为0的节点
int count = 0;//统计节点总数
for(int i=0;i<indegree.size();++i)
if(indegree[i]==0)
zero_q.push(i); //入度为0的点入队列
while(!zero_q.empty())
{
int node = zero_q.front();//出队列
zero_q.pop();
count++;
for(auto elem:graph[node])
{
if(--indegree[elem]==0)//将以该节点为首节点的其他节点入度减1,如果减后入度为0,则入队列
zero_q.push(elem);
}
}
return count==numCourses;
}
};
2.DFS
从图中任一节点开始搜索,如果搜到递归路径上的点就说明有环。
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {//prerequisites的每个元素其实代表一条边
vector<vector<int>> graph(numCourses); //保存图,下标为每个节点,对应的vector为以该节点为首的其他节点
vector<int> visited(numCourses,0); //用于保存一个节点是否被访问过,未访问为0,在当前路径正在被访问为1,在其他路径被访问过为2
for(auto edge:prerequisites)
graph[edge[1]].push_back(edge[0]);//edge[1]为首节点,比如(1,0)表示0指向1,0为首节点
bool has_circle = false;
for(int i=0;i<numCourses;++i) //从任一点开始搜索
{
if(has_circle== true) return false; //有环返回false,不写结果也是对的
if(visited[i]==0) dfs(graph,i,visited,has_circle); //当前点未被访问过,则深度搜索
}
return !has_circle; //考虑最后一次搜索的结果
}
void dfs(const vector<vector<int>>& graph,int node,vector<int>& visited,bool &has_circle)//搜索的图是graph,当前搜到第node个节点了,visited表示每个点是否被搜过
{
if(visited[node]==1) //搜到了之前的节点,存在环
{
has_circle= true;
return;
}
visited[node]=1; //标记当前点为1,表示要开始搜这个点了
for(int i=0;i<graph[node].size();++i) //搜和该点相连的点
{
dfs(graph,graph[node][i],visited,has_circle);
if(has_circle) return; //如果搜到环了就提前停止搜索,不写超出时间限制
}
visited[node]=2; //这里标2是为了说明以该点为首无环,下次不必在搜索了。改成0也可以,只不过时间会增大,因为会重复搜索
}
};
写法模板:
参考:https://blog.csdn.net/jiange_zh/article/details/48183267
BFS:
将所有入度为0的顶点加入队列q;
while(!q.empty() )
{
u = q.front();
q.pop();
list.push(u);
for (u的每个邻接点v)
{
删除边(u, v);
if (indegree(v) == 0)
q.push(v);
}
}
if (图G还有边存在)
return 存在环
else
return list;
DFS:
L ← 用于存放排序结果的数组
S ← 出度为0的顶点的集合
for (S中的每个顶点)
dfs(n)
void dfs(node n)
{
if (!vis[n])
{
vis[n] = true;
for (每一个顶点m,满足m->n)
dfs(m);
}
L.push(n);
}
即拓扑排序或者图的遍历有两种方式:深度优先和广度优先