原题地址
题目要求
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.
题目解释:
输入包括一个整数代表图的大小,还有一个vector保存有向图的边,要求如果一个节点A指向另一个节点B,那么节点B就不能出现在节点A的前面。
这是一个很经典的拓扑排序问题。
拓扑排序
首先,先介绍一下拓扑排序的含义;
拓扑排序是图论中一个很经典的算法;对于一个有向无环图G
进行拓扑排序,就是将G
中的所有节点排成一个线性序列;对于图G
中的每一条边,如边(u,v)
,则表示在我们输出的线性序列中节点u
必须出现在节点v
的前面,这个线性序列就被我们称为满足拓扑排序序列,简称拓扑序列。
需要注意的是,拓扑排序是针对有向无环图的;
拓扑排序的实现也并不复杂:
(1)首先,我们需要选取图中一个入度为0的节点(入度表示某点作为图中边的终点的次数之和)
(2)然后从图中删除以该节点为起点的所有有向边和该节点,此时被删除的有向边的终点的入度减 1;
(3)重复上面的(1)和(2)操作直到图中没有入度为0的节点;
当算法结束后,如果图中仍然还有节点,那么说明该有向图不存在拓扑序列;反之,存在拓扑序列;
下图是拓扑排序的一个简单例子:
由此可以得到拓扑序列{1,2,3,4}
我们还可以看到,在第二步移除节点2和移除节点3的效果是一样的,因此拓扑排序并不是唯一的。
讲完了拓扑排序的原理,下面就是代码实现
代码实现
代码解释已经注释了,应该不难理解:
/************************Graph类************************/
class Graph{
private:
int nums; // 图的顶点个数
std::vector<int>* adj; // 邻接表保存有向边
vector<int> indegree; // 记录每个节点的入度
public:
/*
* 构造函数
* 输入:图的个数
*/
Graph(int v){
nums = v;
// 初始化邻接表和每个节点的入度
adj = new std::vector<int>[nums];
for (int i = 0; i < v; ++i)
{
indegree.push_back(0);
}
}
/* 析构函数 */
~Graph(){
delete []adj;
}
/*
* 函数说明:添加有向边
* 输入:
* from: 起始点
* to: 终点
*/
void addEdge(int from,int to){
// 往邻接表中添加有向边
adj[from].push_back(to);
// 修改终点的入度
indegree[to]++;
}
/*
* 函数说明:判断是否存在拓扑序列
* 输出:布尔值,true表示存在拓扑序列
*/
bool topological_sort(){
// 使用一个队列保存入度为0的节点
queue<int> q;
for(int i = 0;i < nums;i++){
if(indegree[i] == 0){
q.push(i);
}
}
// count记录已经从图中删除的节点个数,当count等于节点个数,说明存在拓扑序列
int count = 0,temp;
while(!q.empty()){ // 跳出循环条件:图中已经不存在入度为0的节点
temp = q.front();
q.pop();
count++;
// 更新入度和保存入度为0的节点的队列
for(auto value:adj[temp]){
indegree[value]--;
if(indegree[value] == 0){
q.push(value);
}
}
}
return (count == nums);
}
};
/************************Solution类************************/
class Solution {
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
// 初始化图
Graph graph(numCourses);
for(auto value:prerequisites){
graph.addEdge(value.first,value.second);
}
// 拓扑排序
return graph.topological_sort();
}
};