Leetcode207--->課程表(逆拓撲排序)


題目: 課程表,有n個課程,[0, n-1];在修一個課程前,有可能要修前導課程;

舉例:

2, [[1,0]]  修課程1前需要先修課程0

There are a total of 2 courses to take. To take course 1 you should 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 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

解題思路:

其本質是逆拓撲排序,因此首先要將給定的課程關系轉換為鄰接表存儲,即將給定的圖轉化為鄰接表;

舉例說明:

4, [[1,0],[2,0],[3,1],[3,2]]

1) 代碼5的for循環是為了初始化一個鄰接鏈表的結構,[[],[],[],[],]
2) 代碼10的for循環是為了找到有那幾個節點要依靠當前節點;其中prerequisites[0][1] = 0,prerequisites[0][0] = 1,表示課程1要依靠課程0;prerequisites[1][1] = 0,prerequisites[1][0] = 2,表示課程2依靠課程0;以此類推,則最終posts中的結果為:
[[1,2],[3],[3],[]]表示課程1,2依靠課程0,課程3依靠課程1,課程3依靠課程2,沒有課程依靠課程3;
3) 代碼15聲明了一個數組preNums,下面代碼16的for循環來告訴我們這個數組是干什么的;
4) 代碼16的for循環中,拿出post中的每一個set,假設此時拿出的是post.get(0),則拿出的是依靠0課程才能修完課程的集合[1,2];該循環結束后,preNums=[0,1,1,2]表示0課程不依賴其他課程(0課程的出度為0),1課程依賴一個課程,2課程依賴一個課程,3課程依賴兩個課程;因此preNums數組表示下標的課程依賴幾個其他的課程
5) 到此為止,我們記錄了下標課程被哪幾個課程依賴,即posts;下標課程自己依賴幾個課程,即preNums數組;到這里可以看出我們使用的逆拓撲結構的思想;即找出preNums數組中為0的那個下標課程(在圖中表示出度為0),代碼27行的for循環就是干這個事情的;
6) 假設此時沒有找到出度為0的那個課程,則表示課程中有相互依賴的情況,則直接return false; 如果找到出度為0的那個課程,在此例子中為課程0,而此時課程0在post中可以得出其被[1,2]課程依賴,因此需要刪除0節點,那么相應的也要刪掉課程1和課程2分別對課程0的依賴,那此時,需要更新preNums數組,即1課程所依賴的課程數減一,
2課程所依賴的課程數減一,則此時preNums = [-1, 0, 0, 2],-1表示已經刪除了0節點。循環5,6)則可以判斷中課程是否可以順利修完,即給出的數組是否是一個逆拓撲結構;
 1 public class Solution {
 2     public boolean canFinish(int numCourses, int[][] prerequisites) {
 3         // init the adjacency list
 4         List<Set> posts = new ArrayList<Set>();
 5         for (int i = 0; i < numCourses; i++) {
 6             posts.add(new HashSet<Integer>());
 7         }
 8 
 9         // fill the adjacency list,找到有哪幾個點要依靠該點
10         for (int i = 0; i < prerequisites.length; i++) {
11             posts.get(prerequisites[i][1]).add(prerequisites[i][0]);
12         }
13 
14         // count the pre-courses
15         int[] preNums = new int[numCourses];  // 計算下標的課程依賴幾個課程
16         for (int i = 0; i < numCourses; i++) {
17             Set set = posts.get(i);
18             Iterator<Integer> it = set.iterator();
19             while (it.hasNext()) {
20                 preNums[it.next()]++;
21             }
22         }
23         // remove a non-pre course each time
24         for (int i = 0; i < numCourses; i++) {
25             // find a non-pre course
26             int j = 0;
27             for ( ; j < numCourses; j++) {   // 找到出度為0的點,刪掉,並更新依靠該點的前驅點的個數
28                 if (preNums[j] == 0) break;
29             }
30 
31             // if not find a non-pre course
32             if (j == numCourses) return false;
33 
34             preNums[j] = -1;
35 
36             // decrease courses that post the course
37             Set set = posts.get(j);
38             Iterator<Integer> it = set.iterator();
39             while (it.hasNext()) {
40                 preNums[it.next()]--;    // 刪除依賴j節點的節點的出度
41             }
42         }
43 
44         return true;
45     }
46 }

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM