如何判断有向图是否有环
- 1.dfs,bfs
- 2.拓扑排序
使用拓扑排序来解决这个问题,首先什么是拓扑排序?一直删除出度为0的顶点直到没有出度为0的顶点,如果最终还有顶点存在就说明有环,并且是由剩下的顶点组成的环。
例如 有有向图的邻接表如下
0->1
1->0
1->2
2->3
首先 3这个顶点出度为 0那先删除跟3有关的邻接表,剩下的邻接表有
0->1
1->0
1->2
然后 2这个顶点出度为0,删除跟2有关的邻接表,剩下的邻接表有
0->1
1->0
已经没有出度为0的邻接表了,剩下的0,1组成了有向图的环
代码实现
// 有向图是否有环
func canFinish(numCourses int, prerequisites [][]int) bool {
// 邻接表map
adj := make(map[int]map[int]struct{}, numCourses)
// 反向邻接表map
adjR := make(map[int]map[int]struct{}, numCourses)
for i := 0; i < len(prerequisites); i++ {
_, ok := adj[prerequisites[i][1]]
if !ok {
adj[prerequisites[i][1]] = make(map[int]struct{}, numCourses)
}
_, ok = adjR[prerequisites[i][0]]
if !ok {
adjR[prerequisites[i][0]] = make(map[int]struct{}, numCourses)
}
adj[prerequisites[i][1]][prerequisites[i][0]] = struct{}{}
adjR[prerequisites[i][0]][prerequisites[i][1]] = struct{}{}
}
// 所有顶点集合map
g := make(map[int]struct{}, numCourses)
for i := 0; i < numCourses; i++ {
g[i] = struct{}{}
}
return !topology(adj, adjR, g)
}
// 图的拓扑排序 一直删除出度为0的顶点直到所有顶点出度大于0或者没有顶点了
func topology(adj, adjR map[int]map[int]struct{}, g map[int]struct{}) bool {
var existsZero bool
for k := range g {
// 出度为0 删除这个节点
if _, ok := adj[k]; !ok {
existsZero = true
delete(g, k)
mr, ok1 := adjR[k]
if ok1 {
for i := range mr {
delete(adj[i], k)
if len(adj[i]) == 0 {
delete(adj, i)
}
}
delete(adjR, k)
}
}
}
if len(g) > 0 && existsZero {
return topology(adj, adjR, g)
}
if len(g) > 0 && !existsZero {
return true
}
return false
}