NO.207 課程表 中等 、NO.210 課程表 II 中等
非常經典的拓撲排序問題
NO.207 課程表 中等
思路一:拓撲排序 拓撲排序的總體思路選擇 廣度優先遍歷+貪心
。
通常拓撲排序有兩個主要功能:
- 得到一條拓撲序列,拓撲序列不唯一。
- 判斷一個有向圖是否有環。
實現上,用兩個集合,分別保存每個節點入度的數量、每個節點的下一個後繼節點。還需要一個隊列,進行廣度優先遍歷。
將沒有前驅的節點(入度爲 0 )入隊,廣搜每出隊一個節點,就將該節點的所有後繼鄰接節點的入度 -1 ,如果入度減爲 0(沒有前驅節點) 則節點入隊。
廣搜過程中記錄出隊節點的數量 count,如果圖中有環,則 count<numCourses
。
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (prerequisites.length == 0) return true;
//保存每個節點的入度
int[] inDegree = new int[numCourses];
//保存每個節點的下一個後繼鄰接節點
HashSet<Integer>[] nextAdjacency = new HashSet[numCourses];
for (int i = 0; i < numCourses; i++) nextAdjacency[i] = new HashSet<>();
//預處理
for (int[] temp : prerequisites) {
inDegree[temp[0]]++;
nextAdjacency[temp[1]].add(temp[0]);
}
//廣搜隊列
Queue<Integer> queue = new LinkedList<>();
//入度爲 0 的節點入隊
for (int i = 0; i < inDegree.length; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
//開始廣搜,記錄出過隊的節點數量
int count = 0;
while (!queue.isEmpty()) {
Integer poll = queue.poll();
count++;
//遍歷出隊節點的所有後繼鄰接節點
for (int next : nextAdjacency[poll]) {
//減少後繼節點的一條入度
inDegree[next]--;
//如果後繼節點的入度爲 0 則入隊
if (inDegree[next] == 0) {
queue.offer(next);
}
}
}
//如果存在環,則 count<numCourses
return count == numCourses;
}
時間複雜度:O(E+V) 邊的數量 E ,節點數量 V。兩次遍歷鄰接表
空間複雜度:O(E+V)
NO.210 課程表 II 中等
相較於前一題,本題需要返回一條拓撲排序的路徑。
思路一:拓撲排序 和上一題的思路一樣,區別在於本題需要記錄出隊的節點序列,而不僅僅是數量。
public int[] findOrder(int numCourses, int[][] prerequisites) {
//保存每個節點的入度
int[] inDegree = new int[numCourses];
//保存每個節點的後繼鄰接節點
List<Integer>[] nextAdjacency = new List[numCourses];
//預處理
for (int i = 0; i < numCourses; i++) nextAdjacency[i] = new LinkedList<>();
for (int[] temp : prerequisites) {
inDegree[temp[0]]++;
nextAdjacency[temp[1]].add(temp[0]);
}
//廣搜隊列
Queue<Integer> queue = new LinkedList<>();
//入度爲 0 的節點入隊
for (int i = 0; i < inDegree.length; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
//保存出隊節點序列 和 數量
int[] res = new int[numCourses];
int count = 0;
//廣搜
while (!queue.isEmpty()) {
int poll = queue.poll();
res[count++] = poll;
//遍歷出隊節點的所有後繼鄰接節點
for (int next : nextAdjacency[poll]) {
//後繼鄰接節點入度-1
inDegree[next]--;
//後繼鄰接節點入度爲 0 則入隊
if (inDegree[next] == 0) {
queue.offer(next);
}
}
}
//是否有環
return count == numCourses ? res : new int[0];
}
時間複雜度:O(E+V) 邊的數量 E ,節點數量 V。兩次遍歷鄰接表
空間複雜度:O(E+V)
本人菜鳥,有錯誤請告知,感激不盡!
更多題解和源碼:github
空間複雜度:O(E+V)
本人菜鳥,有錯誤請告知,感激不盡!
更多題解和源碼:github