目录
- 题目描述
- 主要思路
- 解题过程
- 完整代码
题目描述


这个问题相当于判断有向图中是否存在循环。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。如果没有循环,则可以进行拓扑排序。
主要思路
我们可以通过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;
};
本文介绍了一种基于深度优先搜索(DFS)的拓扑排序算法,用于解决课程学习顺序的问题。通过构建有向图并检测环,确定是否能完成所有课程的学习。文章详细解释了DFS过程,包括变量定义、边的表示方法、执行流程及其实现细节。

被折叠的 条评论
为什么被折叠?



