course schedule算是很典型的拓扑排序问题,sedgewick老爷爷的算法课就是以course schedule作为例子的。leetcode题目点链接可以看到。
我记得这学期算法课期中考试也考到了拓扑排序,可惜那时候我刚转cs对算法的boundary不熟悉,题目简单的变了点花样(描述)我就没认出来其实就是拓扑排序。
Princeton算法课讲拓扑排序用的是dfs算法,后来和同学聊天意识到还有一种常用解法(出入度)。
lc207考察点:
- 图的表达式
- 拓扑排序的两种解法
- cycle detection (只有 acyclic graph can be topological sorted)

我以前只会用dfs做这道题,dfs的cycle detection并不好想,我是看了好久才弄懂。是用了了call stack,call stack是从搜索启示节点nodeInit开始,一层一层往下加,当某个节点nodeX的neighbor nodeY也在call stack中,就说明出现了一个nodeY -> ... nodeX -> nodeY
的cycle
package topological_sort;
public class CourseSchedule {
private boolean[] marked;
private boolean[] inCallStack;
int[][] adjMat;
public boolean canFinish(int numC, int[][] preq) {
// transform input prerequisites into an adjacent matrix;
adjMat = new int[numC][numC];
for (int i = 0; i < preq.length; i++) {
adjMat[preq[i][0]][preq[i][1]] = 1;
}
marked = new boolean[numC];
inCallStack = new boolean[numC];
for (int i = 0; i < numC; i++) {
marked[i] = false;
inCallStack[i] = false;
}
for (int n = 0; n < numC; n++) {
if (marked[n] == true) {
continue;
}
if (dfs(n) == false) {
return false;
}
}
return true;
}
private boolean dfs(int n) { // visit the n^{th} node;
marked[n] = true;
inCallStack[n] = true;
for (int i = 0; i < adjMat[n].length; i++) { // check all the neighboring nodes
if (adjMat[n][i] == 0) { // not neighboring node
continue;
}
if (inCallStack[i] == true) { // neighboring node and currently in CallStack
System.out.println("node " + i + " is in CallStack");
return false;
}
if (marked[i] == false) { // dfs-ly visit the unmarked nodes
if (dfs(i) == false) {
return false;
}
}
}
inCallStack[n] = false;
return true;
}
public static void main(String[] args) {
CourseSchedule cs = new CourseSchedule();
int numCourses = 4;
int[][] prerequisites = new int[4][2];
prerequisites[0] = new int[] {1, 0};
prerequisites[1] = new int[] {2, 1};
prerequisites[2] = new int[] {3, 2};
prerequisites[3] = new int[] {1, 3};
System.out.print(cs.canFinish(numCourses, prerequisites));
}
}
Alternatively, in-out degree方法做cycle-detection实在太容易了。nodes还没有删除完久找不到zero-indegree的node,则有cycle。
package topological_sort;
// solution to CourseSchedule using in-degree and out-degree
public class CourseSchedule_InOutDegree {
int[][] adjMat;
int[] inDegree;
boolean[] isRemoved;
public boolean canFinish(int numC, int[][] preq) {
// setting attributes
adjMat = new int[numC][numC];
inDegree = new int[numC];
for (int i = 0; i < preq.length; i++) {
adjMat[preq[i][0]][preq[i][1]] = 1;
inDegree[preq[i][1]]++;
}
isRemoved = new boolean[numC];
for (int i = 0; i < numC; i++) {
isRemoved[i] = false;
}
// remove node/edges one by one
while (pickZeroInDegree() >= 0) {
removeNode(pickZeroInDegree());
}
// check result;
return allRemoved();
}
private void removeNode(int n) { // the node n has 0 inDegree,
for (int i = 0; i < adjMat[n].length; i++) {
if (adjMat[n][i] == 0) { // not neighbor node
continue;
}
inDegree[i]--; // remove that edge <n, i>
}
isRemoved[n] = true;
}
private int pickZeroInDegree() { // pick a node with zero degree. return -1 if none;
for (int i = 0; i < inDegree.length; i++) {
if (inDegree[i] == 0 && !isRemoved[i]) {
return i;
}
}
return -1;
}
private boolean allRemoved() { // check if all nodes are removed.
for (int i = 0; i < isRemoved.length; i++) {
if (isRemoved[i] == false) {
return false;
}
}
return true;
}
public static void main(String[] args) {
CourseSchedule_InOutDegree cs = new CourseSchedule_InOutDegree();
int numCourses = 3;
int[][] prerequisites = new int[3][2];
prerequisites[0] = new int[] {0, 1};
prerequisites[1] = new int[] {1, 2};
prerequisites[2] = new int[] {2, 0};
System.out.print(cs.canFinish(numCourses, prerequisites));
}
}
网友评论