演算法概論week4 | 210. Course Schedule II
阿新 • • 發佈:2018-12-13
目錄
- 題目描述
- 主要思路
- 解題過程
- 完整程式碼
題目描述
這個問題相當於判斷有向圖中是否存在迴圈。如果存在迴圈,則不存在拓撲排序,因此不可能選取所有課程進行學習。如果沒有迴圈,則可以進行拓撲排序。
主要思路
我們可以通過DFS進行拓撲排序。回憶一下DFS的主要過程:
procedure dfs(G)
for all v ∈ V:
visited(v) = false
for all v ∈ V:
if not visited(v) : explore(v)
procedure explore(G, v)
visited(v) = true
previsited(v)
for each edge (v, u) ∈ E :
if not visited(u) : explore(u)
postvisited(v)
如果存在回邊,就表示有環,不能進行拓撲排序;否則,按post值降序排列得到的陣列就是一種拓撲排序結果。
解題過程
1.變數解釋
剛開始我想使用struct來表示每個節點的pre、post、visited值,後來發現還是分別用陣列儲存比較方便:
vector<int> pre(numCourses);
vector<int> post(numCourses);
vector< int> visited(numCourses);
在這題裡,我用另一種方式表示邊:
vector<vector<int>> edges(numCourses);
for(int i = 0; i < prerequisites.size(); i++) {
edges[prerequisites[i].second].push_back(prerequisites[i].first);
}
edges[i]表示節點i所指向的節點的集合。
count與num_unsorted是私有變數,count用於記錄pre或post值,num_unsorted表示未排序的節點數 - 1,每當一個節點獲得了post值,我們就可以把這個節點放入結果陣列中,而num_unsorted就是該節點的下標:
count = 0;
num_unsorted = numCourses - 1;
2.過程解釋 對每個節點進行explore,如果發現一個迴圈,則返回空陣列:
for(int i = 0; i < numCourses; i++) {
if(!explore(i, edges, res, pre, post, visited)) {
return {};
}
}
在explore中,如果一個節點已經被訪問了,則直接返回true:
if(visited[i]) return true;
對節點i指向的每個節點j,如果j是i的祖先,則然會false,否則,如果還未訪問j,則訪問j,如果在explore j 時發現迴圈,則返回false:
for(int j = 0; j < edges[i].size(); j++) {
if(visited[edges[i][j]] && post[edges[i][j]] == 0) {
return false;
}
if(!visited[edges[i][j]]) {
if(!explore(edges[i][j], edges, res, pre, post, visited)) {
return false;
}
}
}
這個演算法應該是較好地實現了書上的演算法,執行用時12ms:
完整程式碼
class Solution {
public:
vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> pre(numCourses);
vector<int> post(numCourses);
vector<int> visited(numCourses);
vector<vector<int>> edges(numCourses);
vector<int> res(numCourses);
count = 0;
num_unsorted = numCourses - 1;
for(int i = 0; i < prerequisites.size(); i++) {
edges[prerequisites[i].second].push_back(prerequisites[i].first);
}
for(int i = 0; i < numCourses; i++) {
if(!explore(i, edges, res, pre, post, visited)) {
return {};
}
}
return res;
}
bool explore(int i, vector<vector<int>>& edges, vector<int>& res, vector<int>& pre, vector<int>& post, vector<int>& visited) {
if(visited[i]) return true;
pre[i] = ++count;
visited[i] = true;
for(int j = 0; j < edges[i].size(); j++) {
if(visited[edges[i][j]] && post[edges[i][j]] == 0) {
return false;
}
if(!visited[edges[i][j]]) {
if(!explore(edges[i][j], edges, res, pre, post, visited)) {
return false;
}
}
}
post[i] = ++count;
res[num_unsorted] = i;
num_unsorted--;
return true;
}
private:
int count;
int num_unsorted;
};