拓扑排序常用于在具有先序关系的任务规划中,解决此类问题需要先把图勾画出来。
LeetCode适用题目:
207 课程安排的合法性
要求:一个课程可能会先修课程,判断给定的先修课程规定是否合法。
210 课程安排的顺序
要求:输出修完所有课程的顺序
例如:
输入:[[1,0],[2,0],[3,1],[3,2]](每个数组的第二个元素为先修课程)
输出:先修课程合法,顺序为[0,1,2,3]或者[0,2,1,3]
解题思路:先用List[]构造图,每位对应的List存储以这门课程为先修课程的课
接下来可以用DFS或者BFS。
DFS:设置全局变量标记每门课程是否已经修过, 以每个课程为起点,设置局部变量判断是否有环,如果没有环就把这门课标记,接着遍历以这门课为先修课程的所有课,如果没有环则将这门课程标记为已修.
207 课程安排的合法性的DFS解法:
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<Integer>[] graph = new List[numCourses];
for(int i = 0;i<graph.length;i++){
graph[i] = new ArrayList<Integer>();
}
boolean[] visit = new boolean[numCourses];
for(int[] pre:prerequisites){
graph[pre[1]].add(pre[0]);
}
for(int i = 0;i<numCourses;i++){
if(!dfs(graph,visit,i,new boolean[numCourses]))
return false;
}
return true;
}
public boolean dfs(List<Integer>[] graph,boolean[] visit,int course,boolean[] isLoop){
if(visit[course])
return true;
if(isLoop[course])
return false;
isLoop[course] = true;
for(int i = 0;i<graph[course].size();i++){
if(!dfs(graph,visit,graph[course].get(i),isLoop))
return false;
}
visit[course] = true;
return true;
}
210 课程安排的顺序的DFS解法
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer>[] graphic = new List[numCourses];
for (int i = 0; i < numCourses; i++) {
graphic[i] = new ArrayList<>();
}
for (int[] pre : prerequisites) {
graphic[pre[0]].add(pre[1]);
}
Stack<Integer> postOrder = new Stack<>();
boolean[] globalMarked = new boolean[numCourses];
boolean[] localMarked = new boolean[numCourses];
for (int i = 0; i < numCourses; i++) {
if (hasCycle(globalMarked, localMarked, graphic, i, postOrder)) {
return new int[0];
}
}
int[] orders = new int[numCourses];
for (int i = numCourses - 1; i >= 0; i--) {
orders[i] = postOrder.pop();
}
return orders;
}
private boolean hasCycle(boolean[] globalMarked, boolean[] localMarked, List<Integer>[] graphic,
int curNode, Stack<Integer> postOrder) {
if (localMarked[curNode]) {
return true;
}
if (globalMarked[curNode]) {
return false;
}
globalMarked[curNode] = true;
localMarked[curNode] = true;
for (int nextNode : graphic[curNode]) {
if (hasCycle(globalMarked, localMarked, graphic, nextNode, postOrder)) {
return true;
}
}
localMarked[curNode] = false;
postOrder.push(curNode);
return false;
}
BFS:用degree数组来存储修每门课程之前需要完成的课程数,把需要提前完成的课程数目为0的课添加到队列中,每释放队列的一个元素代表修完了一门课程,然后遍历以这门课程为先决课程的课,对应的degree减一,当degree为0时表示这门课也可以修了,就把它添加到队列中。
207 课程安排的合法性的BFS解法:
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<Integer>[] graph = new List[numCourses];
for(int i = 0;i<graph.length;i++){
graph[i] = new ArrayList<Integer>();
}
int[] degree = new int[numCourses];
for(int[] pre:prerequisites){
degree[pre[0]]++;
graph[pre[1]].add(pre[0]);
}
Queue<Integer> queue = new LinkedList<Integer>();
for(int i = 0;i<numCourses;i++){
if(degree[i]==0)
queue.offer(i);
}
int visit = 0;
while(!queue.isEmpty()){
int from = queue.poll();
visit++;
for(int to:graph[from]){
degree[to]--;
if(degree[to]==0)
queue.offer(to);
}
}
return visit==numCourses?true:false;
}
210 课程安排的顺序的BFS解法
public int[] findOrder(int numCourses, int[][] prerequisites) {
int[] ans = new int[numCourses];
List<Integer>[] graph = new List[numCourses];
for(int i = 0;i<graph.length;i++){
graph[i] = new ArrayList<Integer>();
}
int[] degree = new int[numCourses];
for(int[] pre:prerequisites){
degree[pre[0]]++;
graph[pre[1]].add(pre[0]);
}
Queue<Integer> queue = new LinkedList<Integer>();
for(int i = 0;i<numCourses;i++){
if(degree[i]==0)
queue.add(i);
}
int visit = 0;
while(!queue.isEmpty()){
int from = queue.poll();
ans[visit++] = from;
for(int to:graph[from]){
degree[to]--;
if(degree[to]==0)
queue.add(to);
}
}
return visit==numCourses?ans:new int[0];
}