1. 程式人生 > >演算法概論week4 | 210. Course Schedule II

演算法概論week4 | 210. Course Schedule II

目錄

  • 題目描述
  • 主要思路
  • 解題過程
  • 完整程式碼

題目描述

在這裡插入圖片描述 在這裡插入圖片描述

這個問題相當於判斷有向圖中是否存在迴圈。如果存在迴圈,則不存在拓撲排序,因此不可能選取所有課程進行學習。如果沒有迴圈,則可以進行拓撲排序。

主要思路

我們可以通過DFS進行拓撲排序。回憶一下DFS的主要過程:

procedure dfs(G)

 for all v ∈ Vvisited(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;
};