There are a total of n courses you have to take, labeled from 0
to n - 1
.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.
There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1]
4, [[1,0],[2,0],[3,1],[3,2]]
There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]
. Another correct ordering is[0,2,1,3]
.
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
題意
共有n節課程,課程編號從0至n-1。一些課程可能有先修要求,比如修0號課程前需要先修1號課程,表示為[0,1]
。題目給出課程數和先后修課程對,要求返回完成所有課程的順序。可能有很多正確的上課順序,你只需返回其中一種。如果不可能完成,則返回空數組。
比如:
2, [[1,0]]
共有2節課程。選修1號課程前需要先修0號課程,故上課順序為[0,1]
。
4, [[1,0],[2,0],[3,1],[3,2]]
共有4節課程。正確的上課順序為[0,2,1,3]
。
提示:
輸入的prerequisites是由邊表示的圖,而非鄰接矩陣。
分析
此題是Leetcode207-Course Schedule的進階版,前者只需得出能否完成所有課程即可,本題還需要給出上課順序。實際上這是一個求拓撲排序的題(至于是拓撲全序還是拓撲偏序,是不一定的)。可以有2種求解方法。
第一種:采用bfs。前處理時得到每個節點的入度。每次將入度為0的節點入隊。出隊時將出隊節點的鄰接節點的入度減1,并將出隊節點編號計入answer數組中。循環該過程直到隊列為空。如仍有節點未被訪問,則說明課程關系圖中出現了環,不可能完成所有課程。否則,answer中的編號順序就是上課順序。
第二種:采用dfs。需要一個onStack數組紀錄dfs的路徑上的節點,用于判斷環的存在。而isVisited數組責紀錄dfs中訪問過的節點。而其reversePostOrder就是上課順序。具體可以google搜索reversePostOrder。這種方法比較難理解,但很有意思。最終給出的AC代碼也是第二種方法。
注意
值得注意的是,特殊情況輸入numCourses = 1,prereqisities為空數組時,答案為
[0]
,只有1個節點,并沒有邊的存在。第二種方法中onStack在每次結束本次遞歸時,需要變為false,表示這條路徑上后面的節點在后面的判斷環中,不會被認為是同一路徑上的節點。(感覺好繞)
AC代碼
class Solution {
public:
vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> answer;
if (numCourses == 1) {
answer.push_back(0); return answer;
}
vector<vector<int>> map(numCourses, vector<int>());
stack<int> reversePostOrder;
for (int i = 0; i < prerequisites.size(); ++i) {
map[prerequisites[i].second].push_back(prerequisites[i].first);
}
vector<bool> isVisited(numCourses, false);
for (int i = 0; i < numCourses; ++i) {
if (!isVisited[i]) {
vector<bool> onStack(numCourses, false);
if (hasCycle(map, i, isVisited, onStack, reversePostOrder))
return answer;
}
}
while (!reversePostOrder.empty()) {
int index = reversePostOrder.top(); reversePostOrder.pop();
answer.push_back(index);
}
return answer;
}
bool hasCycle(vector<vector<int>> &map, int i, vector<bool> &isVisited, vector<bool> &onStack, stack<int> &reversePostOrder) {
isVisited[i] = true;
onStack[i] = true;
for (int k : map[i]) {
if (onStack[k])
return true;
else if (!isVisited[k])
if (hasCycle(map, k, isVisited, onStack, reversePostOrder))
return true;
}
onStack[i] = false;
reversePostOrder.push(i);
return false;
}
};