题目地址:
https://www.lintcode.com/problem/course-schedule-iv/description
给定一个 n n n个顶点的简单有向图,求一共有多少个不同的拓扑排序。
思路是DFS。枚举其所有可能的拓扑排序然后计数即可。具体来讲就是,先建图,并且记录每个顶点的入度。接着开始DFS,在每一个递归层,都去枚举该层可以选哪个顶点。显然能选择的顶点必须满足两个条件:第一,之前未选过;第二,入度为 0 0 0;每次选择好了顶点后,在进入下一层递归之前,需要将其出边删掉(也就是将其邻接点的入度减去 1 1 1),然后标记其为选择过;递归返回之前记得恢复现场。代码如下:
public class Solution {
// 存拓扑序的计数
private int res;
/**
* @param n: an integer, denote the number of courses
* @param p: a list of prerequisite pairs
* @return: return an integer,denote the number of topologicalsort
*/
public int topologicalSortNumber(int n, int[][] p) {
// Write your code here
// 用邻接矩阵建图并且记录每个顶点的入度
boolean[][] graph = new boolean[n][n];
int[] indegrees = new int[n];
for (int[] edge : p) {
graph[edge[1]][edge[0]] = true;
indegrees[edge[0]]++;
}
// 从第0层开始遍历
dfs(0, graph, indegrees, new boolean[n], n);
return res;
}
// count记录已经选择了多少个顶点
private void dfs(int count, boolean[][] graph, int[] indegrees, boolean[] visited, int n) {
// 如果已经选择了n个顶点,说明找到了一个拓扑序,计数加一
if (count == n) {
res++;
return;
}
// 接下来枚举当前位置可以放哪个顶点
for (int i = 0; i < n; i++) {
// 如果之前未选过,并且其入度为0,则说明该顶点可以选
if (!visited[i] && indegrees[i] == 0) {
// 标记其为选过,并将其出边全删掉
visited[i] = true;
for (int j = 0; j < n; j++) {
if (graph[i][j]) {
indegrees[j]--;
}
}
// 进入下一层递归
dfs(count + 1, graph, indegrees, visited, n);
// 恢复现场
for (int j = 0; j < n; j++) {
if (graph[i][j]) {
indegrees[j]++;
}
}
visited[i] = false;
}
}
}
}
时间复杂度由于有剪枝所以很难估计。空间复杂度 O ( V 2 ) O(V^2) O(V2)。