原题地址:https://leetcode-cn.com/problems/course-schedule/description/
题目描述:
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
说明:
输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
提示:
这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
通过 DFS 进行拓扑排序 - 一个关于Coursera的精彩视频教程(21分钟),介绍拓扑排序的基本概念。
拓扑排序也可以通过 BFS 完成。
解题方案:
这道题涉及了很多话题:dfs、bfs、图以及拓扑排序。解题思路如下:
- 首先构造有向图,构造图有三种方式:关联矩阵,邻接矩阵和邻接表。这里采用的是邻接表,因为要采取很多的插入操作。
- 接着,采用bfs进行拓扑排序,学习拓扑排序的方法,每次筛选时,删除度为0的节点。
- 最后是判定,若存在度不为0的节点,则说明存在着环。
代码:
class Solution {
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> heads(numCourses, -1), degree(numCourses, 0), points, args;
pair<int, int> p;
int from, to, count = 0, len = prerequisites.size();
/* 构造有向图,邻接表 */
for (int i = 0; i < len; ++i) {
p = prerequisites[i];
from = p.second;
to = p.first;
++degree[to];
args.push_back(heads[from]);
points.push_back(to);
heads[from] = count++;
}
/* bfs拓扑排序,依次移除入度为0的点 */
queue<int> q;
for (int i = 0; i < numCourses; ++i)
if (degree[i] == 0) q.push(i);
while (!q.empty()) {
from = q.front();
q.pop();
to = heads[from];
while (to != -1) {
if(--degree[points[to]] == 0) q.push(points[to]);
to = args[to];
}
}
/* 判定是否所有的点入度都为0,若是则不存在环,否则存在环 */
for (int i = 0; i < numCourses; ++i)
if (degree[i] > 0)
return false;
return true;
}
};
今天是10.24,程序员日??说实话,真的没有感觉,对程序员没啥认同感。。。
没想到大家这么重视,CSDN送一枚徽章还是美滋滋的,哈哈哈。。。