1. 有向图中是否存在环
有向图中是否存在环的问题,可以用图的拓扑排或者深度遍历图看是否存在某个节点是否被访问过两次来解决(深度遍历这边先不具体说,后面会专门分析深度遍历这个算法)
图的拓扑排序简单来说就是将图中所有点的入读进行计算,每次弹出一个入读为0的点,然后把该点指向的其他点的入度-1,重复上述操作,一直到把所有的点都弹出。
举例说明图的拓扑排序
拓扑排序可能有很多种情况,比如说上面的图拓扑排序可以是abcdhefg,也可以是ahbecfdg,但绝对不可能是abehcfdg,因为ab去掉之后e的入度还不是0,还不能被输出。
实现拓扑排序的方法其实主要依托于遍历图的方法,广度优先和深度优先的遍历方法都可以,图是否有换之所以能用拓扑排序的方法来解决就是因为有环的图一定存在入读永远都不会为0的节点,所以有环的图是没有办法进行完全的拓扑排序的一定会出现拓扑排序输出的节点数和图中一共有的节点数不相等的情况,下面分别来介绍一下广度优先和深度优先再拓扑排序中的方法
(1) 广度优先
1) 先计算出所有节点的入度
2) 使用一个队列将入度为0的保存起来
3) 弹出队列的第一个节点把这个节点指向的其他节点的入度-1,然后把入度-1后为0的节点放进队列
4) 循环3)知知道队列为空
(2) 深度优先
1) 先计算出所有节点的入度
2) 使用一个栈将入度为0的保存起来
3) 弹出栈顶节点把这个节点指向的其他节点的入度-1,然后把入度-1后为0的节点放进栈
4) 循环3)知直到栈为空
这边如果队列或者栈空了,但是被访问过的节点数小于图中节点总数则图中有环。以leetcode上的一道题展示代码
Leetcode 207. Course Schedule 课程是否能全部学完
There are a total of n coursesyou have to take, labeled from 0
to n- 1
.
Some courses may have prerequisites, for example to take course0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs,is it possible for you to finish all courses?
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 youshould have finished course 0. So it is possible.
2, [[1,0],[0,1]]
There are a total of 2 courses to take. To take course 1 youshould have finished course 0, and to take course 0 you should also havefinished course 1. So it is impossible.
Note:
- The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
- You may assume that there are no duplicate edges in the input prerequisites.
public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[][] f = new int[numCourses][numCourses];
int[] degree = new int[numCourses];
for(int i = 0; i< prerequisites.length; i++)
f[prerequisites[i][0]][prerequisites[i][1]] = 1;
for(int i = 0; i< numCourses; i++){
for(int j = 0; j < numCourses; j++){
if(f[i][j] == 1) degree[j]++;
}
}
Stack
stack = new Stack
();
for(int i = 0; i < numCourses; i++){
if(degree[i] == 0) stack.add(i);
}
int count = 0;
while(!stack.isEmpty()){
int index = stack.pop();
count++;
for(int i = 0; i < numCourses; i++){
if(f[index][i] == 1) {
degree[i]--;
if(degree[i] == 0) stack.add(i);
}
}
}
return count == numCourses;
}
}
2. 无向图中是否有环
考虑有向图的有环问题,对于有向图来说遍历过程中一个点出现了没有其他点指向它的情况,那么它就一定不是环的一部分,对于无向图引申过来可以认为在遍历过程中如果一个点出现它只与其他另外的一个点有关联了,这个时候它的度为1,那么认为它一定不在环中,反过来想如果无向图有环那么即使不停的做度-1的操作,也一定会存在度永远不为1的点(包括自己连着自己的点)。那么对于无向图有环的过程可以写成:
1) 用邻接矩阵保存图信息
2) 将度为1的点放入一个队列中
3) 弹出队列的一个节点,将与他有边的其他的点的度-1,将度-1后变为1的点加入到队列中
重复3这个操作直到队列为空,访问过得节点数和图中总结点数不相同则证明图中有环。