-
进行练习的题目是207. 课程表, 210. 课程表 II
-
什么是拓扑排序呢?给定一个包含n个节点有向图G,所谓拓扑排序就是给出它的节点编号的一种排序。在排序中需要满足,对于G中任何一条有向边(u, v),u在排序中都出现在v的前面。
-
简而言之,如果G中有环,那就不存在拓扑排序,同时,拓扑排序也不止一种形式。
使用DFS
- 思路:对于一个节点,如果它的相邻节点都被访问完成,则回溯到它的时候,它也被访问完成,并将其放入ans。由递归的特性可以知道,此时,在这个点之前的点都已经在ans中了,也就是说,ans的反向就是一个拓扑排序的结果。
- 访问状态有三种:[0]:未访问,[1]:访问中,[2]:访问完成
- 所以,在dfs的时候,该点的邻边访问状态为0时,对其访问;为1时,遇到环,返回失败;为2时,不需要动作。
- 给出210题的DFS解答,207题的解答类似。
class Solution {
private:
vector<vector<int>> edge; //用于记录边的信息
vector<int> visited; //用于记录访问状态
bool hasCircle = false; //用于记录是否有环
vector<int> ans;
public:
void dfs(int i) {
visited[i] = 1;
for (int num: edge[i]) {
if (visited[num] == 0) {
dfs(num);
if(hasCircle)
return;
} else if (visited[num] == 1) {
hasCircle = true;
return;
}
}
visited[i] = 2;
ans.push_back(i);
}
vector<int> findOrder(int numCourses, vector<vector<int>> &prerequisites) {
//进行vector的初始化,也可以使用resize()
edge = vector<vector<int>>(numCourses);
visited = vector<int>(numCourses);
//首先,将题目给的信息转化为邻接矩阵
for (auto& pair: prerequisites) {
//让先修课指向之后的课程
edge[pair[1]].push_back(pair[0]);
}
for (int i(0); i < numCourses; i++)
if (!visited[i])
dfs(i);
reverse(ans.begin(), ans.end());
if (ans.size() == numCourses)
return ans;
else return {};
}
};
使用DFS
- BFS是逆向思维:最先被放进去的是在拓扑排序中最后面的节点。也可以使用正向思维,顺序生成拓扑排序的结果,就是DFS了。
- 思路:在拓扑排序最开始的点,一定是没有入度的,将该节点加入队列。对队列中的元素进行访问后,将其指向的点的入度-1,即代表它的相邻节点少了一个入度(即为没有先修课了),当相邻节点的入度为0时,它就到了最开始的样子。继续这个过程,直到答案中包含所有的节点或者不存在没有入边的节点。
- 同样的,给出210题的DFS解答
class Solution {
private:
vector<vector<int>> edges;
vector<int> indegree;
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges = vector<vector<int>>(numCourses);
indegree = vector<int>(numCourses);
for (auto& pair: prerequisites) {
edges[pair[1]].push_back(pair[0]);
indegree[pair[0]]++;
}
queue<int> sto;
for(int i(0); i < numCourses; i++)
if (indegree[i] == 0)
sto.push(i);
vector<int> ans;
while (!sto.empty()) {
int visitedNow = sto.front();
sto.pop();
ans.push_back(visitedNow);
for (int i: edges[visitedNow]) {
indegree[i]--;
if(!indegree[i])
sto.push(i);
}
}
if (ans.size() == numCourses)
return ans;
else return {};
}
};
复杂度分析
- BFS做法:时间复杂度和空间复杂度都为O(m + n)。O(m)的空间用来存边的关系,O(n)的空间用来存结果。
- DFS做法:同上。