一、原文
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, is it possible for you to finish all courses?
Example 1:
Input: 2, [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input prerequisites.
二、翻译
你必须参加 n 门课程,从 0 开始标记,直到 n-1。
有些课程可能有前提条件,比如如果你想选修标号为0的这门课,你必须先修过标号为1的课,这种关系用这种数对表示:[0, 1]。
给定课程总数和前提条件数对,你是否能完成所有课程?
balabala~
三、分析
3.1 利用拓扑排序
从题意中可以明显看出这是一个(有向)图的问题,再细想一下就会发现这是拓扑排序问题。
所以用一个最简单的方法就是找出所有入度为 0 的结点,然后从它们开始遍历整个图,再遍历过程中对其他结点的入度进行减的操作,如果减到0就入栈(或入队),最后判断是否有入度不为0的存在,如果有,就说明图中出现了环,也就是说此例为FALSE。
当然,在一开始要对输入数据进行一下处理,将数对转换为图的邻接表形式,此处的邻接表是用二维矩阵表示,第一个下标表示尾,存储的值就是与该尾结点相连的头结点(有向图中的边)。
3.2 DFS
既然LeetCode将这题划分在DFS内,那就用DFS来解一下。
首先,要判断能不能完成所有课程,转换一下问题就是判断是不是存在环。如果存在环说明不能完成所有课程,反之就是可以完成所有课程。
那么如何去实现代码呢?
首先,对输入的数对我们进行一些处理(因为它太杂乱了),将其变得适合我们去解决问题的数据形式。
既然我们是判断是否存在环,那我们就得对每门课进行DFS,当然得对每门课进行标记,不然如何知道这门课是否被访问过,这样就得有两个状态:访问过和未访问过。但是两个状态不足以我们判断环的存在,比如 [0, 1], [1, 0],这个用两个状态是无法判断环的存在,所以得需要第三个状态来表示这个结点正在被访问。(此处这个关系书写语言描述我实在不知道咋写好,大家可以自己在纸上模拟一下 T_T)。
具体的见代码吧,代码说话!
四、AC代码
4.1 利用拓扑排序
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
if (prerequisites.size() < 2) {
return true;
}
vector<vector<int>> adjTable(numCourses);
vector<int> in(numCourses, 0);
for (int i = 0; i < prerequisites.size(); ++i) {
adjTable[prerequisites[i][1]].push_back(prerequisites[i][0]);
in[prerequisites[i][0]]++;
}
stack<int> start;
for (int i = 0; i < numCourses; ++i) {
if (in[i] == 0) {
start.push(i);
}
}
while (!start.empty()) {
int s = start.top();
start.pop();
for (int i = 0; i < adjTable[s].size(); ++i) {
in[adjTable[s][i]]--;
if (!in[adjTable[s][i]]) {
start.push(adjTable[s][i]);
}
}
}
for (int i = 0; i < numCourses; ++i) {
if (in[i] != 0) {
return false;
}
}
return true;
}
};
4.2 DFS
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
if (prerequisites.size() < 2) {
return true;
}
vector<vector<int>> adjTable(numCourses);
// 0 表示未访问
// 1 表示正在访问,此处是为了判断循环
// 2 表示访问过了
vector<int> visited(numCourses, 0);
for (int i = 0; i < prerequisites.size(); ++i) {
adjTable[prerequisites[i][1]].push_back(prerequisites[i][0]);
}
for (int i = 0; i < numCourses; ++i) {
if (visited[i] == 0 && !dfs(i, visited, adjTable)) {
return false;
}
}
return true;
}
bool dfs(int course, vector<int>& visited, vector<vector<int>>& adjTable) {
visited[course] = 1;
for (int c : adjTable[course]) {
if (visited[c] == 1) {
return false;
}
if (!dfs(c, visited, adjTable)) {
return false;
}
}
visited[course] = 2;
return true;
}
};
如有错误之处,敬请指出!大家共勉!