/**
* 全局变量
*/
private int[] inDegree; // 记录每个顶点的入度
private List<Integer>[] graph; // 有向图
/**
* 根据边的关系构造有向图并初始化顶点入度表
*
* @param numCourses
* @param prerequisites
* @return
*/
private void createGraphByAdjList(int numCourses, int[][] prerequisites) {
graph = new LinkedList[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new LinkedList<>();
}
inDegree = new int[numCourses]; // 初始时所有顶点入度为0
for (int[] pre : prerequisites) {
int from = pre[1], to = pre[0];
graph[from].add(to);
inDegree[to]++; // to顶点有前驱顶点from,故入度加1
}
}
/**
* 法一(拓扑排序)
* (1)拓扑排序,是将有向无环图G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,
* 若边(u,v)∈E(G),则u在线性序列中出现在v之前
* (2)若拓扑排序失败,则说明不是有向无环图
*
* @param numCourses
* @param prerequisites
* @return
*/
public int[] findOrder(int numCourses, int[][] prerequisites) {
createGraphByAdjList(numCourses, prerequisites); // 初始化有向图和顶点入度表
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) { // 将所有入度为0的顶点入队
if (inDegree[i] == 0) {
queue.offer(i);
}
}
int num = 0; // 记录加入拓扑序列的顶点数
int[] order = new int[numCourses]; // 拓扑序列
while (!queue.isEmpty()) {
int from = queue.poll(); // 取队首入度为0的顶点from
order[num++] = from; // 将顶点from加入拓扑序列
for (int to : graph[from]) { // 遍历顶点from的所有后继顶点
inDegree[to]--; // 后继顶点to入度减1
if (inDegree[to] == 0) { // 若后继顶点to入度减为0,则入队
queue.offer(to);
}
}
}
return num == numCourses ? order : new int[0]; // 加入拓扑序列的顶点数为numCourses,说明拓扑排序成功
}
/**
* 全局变量
*/
private List<Integer>[] graph; // 有向图
private int[] visited; // 记录DFS找环过程中的访问状态, 0:未访问,1:访问中,2:完成访问
private boolean hasCircle = false; // 记录有向图是否有环
private int[] order; // 拓扑序列
private int num = 0; // 加入拓扑序列的顶点数
/**
* 根据边的关系构造有向图,并且用逆邻接表表示
*
* @param numCourses
* @param prerequisites
* @return
*/
private void createGraphByInverseAdjList(int numCourses, int[][] prerequisites) {
graph = new LinkedList[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new LinkedList<>();
}
for (int[] pre : prerequisites) {
int from = pre[1], to = pre[0];
graph[to].add(from); // 逆邻接表
}
}
/**
* DFS需要通过逆邻接表实现,当访问一个顶点的时候,应该递归访问它的前驱顶点,直至前驱顶点没有前驱顶点为止
*
* @param to
* @return
*/
private void findCircle(int to) {
visited[to] = 1; // 正在访问顶点to
for (int from : graph[to]) { // 遍历顶点to的所有前驱顶点
if (visited[from] == 0) { // 若顶点to的前驱顶点from还没有访问过,则访问顶点from
findCircle(from);
} else if (visited[from] == 1) { // 访问途中若顶点to的前驱顶点访问状态为1,则说明有环
hasCircle = true;
return;
}
}
visited[to] = 2; // 顶点to的所有前驱顶点访问完回溯后才算完成顶点to的访问
order[num++] = to; // 将顶点to加入拓扑序列
}
/**
* 法二(DFS),较快
* 通过DFS检测有向图中是否有环,只要存在环,课程就不能完成
*
* @param numCourses
* @param prerequisites
* @return
*/
public int[] findOrder_2(int numCourses, int[][] prerequisites) {
createGraphByInverseAdjList(numCourses, prerequisites); // 初始化有向图
visited = new int[numCourses]; // 初始时所有顶点的访问状态为0
order = new int[numCourses]; // 初始化拓扑序列
for (int i = 0; i < numCourses; i++) {
if (visited[i] == 0) { // 如果顶点i没有访问过,则访问顶点i
findCircle(i);
}
}
return hasCircle ? new int[0] : order; // 有环说明拓扑排序失败,无环说明拓扑排序成功
}
/**
* 210. 课程表 II
*/
lay.showTitle(210);
Solution210 sol210 = new Solution210();
int numCourses210 = 4;
int[][] prerequisites210 = new int[][]{{1, 0}, {2, 0}, {3, 1}, {3, 2}};
arrayOpt.showIntTwoDimArray(prerequisites210, prerequisites210.length);
int[] order210_1 = sol210.findOrder(numCourses210, prerequisites210);
System.out.println(Arrays.toString(order210_1));
int[] order210_2 = sol210.findOrder_2(numCourses210, prerequisites210);
System.out.println(Arrays.toString(order210_2));
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- hzar.cn 版权所有 赣ICP备2024042791号-5
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务