您的位置:首页 > 其它

LeetCode -- Course Schedule

2015-07-22 15:46 357 查看
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?

For example:

2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

2, [[1,0],[0,1]]
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.


查阅题目知:实际上是求一个图中是否存在环。可以考虑使用拓扑排序。

拓扑排序:

拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。

这样说,可能理解起来比较抽象。下面通过简单的例子进行说明!

例如,一个项目包括A、B、C、D四个子部分来完成,并且A依赖于B和D,C依赖于D。现在要制定一个计划,写出A、B、C、D的执行顺序。这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的。

在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面。

拓扑排序算法的基本步骤:

1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);

2. 把所有没有依赖顶点的节点放入Q;

3. 当Q还有顶点的时候,执行下面步骤:

3.1 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);

3.2 对n每一个邻接点m(n是起点,m是终点);

3.2.1 去掉边

public class Solution {
/*
* 拓扑排序
* 1. 选择入度为0的点
* 2. 将选中的点从图中去掉
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
//图使用邻接矩阵表示
int[][] E = new int[numCourses][numCourses];
//顶点的入度
long[] degree = new long[numCourses];

if(numCourses <= 1 ){
return true;
}

//构造图矩阵
//E[i][j]表示i的前驱是j
for(int i = 0 ; i < prerequisites.length ; i ++){
E[prerequisites[i][0]][prerequisites[i][1]] = 1;
degree[prerequisites[i][1]] ++;
}

//初始化入度
for(int j = 0;j < numCourses; j ++){
for(int i = 0; i < numCourses;i++){
if(E[i][j] == 1){
degree[j] ++;
}
}
}
//判断
while(true){
int var = select0(degree);
if(var > -1){
delete0(var,E,degree);
}else{
break;
}
}

for(int i = 0; i < degree.length;i ++){
if(degree[i] != Long.MAX_VALUE){
return false;
}
}
return true;
}
//选择入度为0
public int select0(long[] degree){
for(int i = 0; i < degree.length;i++){
if(degree[i] == 0){
//degree[i] = Long.MAX_VALUE;
return i;
}
}
return -1;
}
//将点从图中去掉
public void delete0(int var, int[][] E,long[] degree){

degree[var] = Long.MAX_VALUE;

for(int j = 0; j < E.length;j++){
if(E[var][j] == 1){

degree[j]--;
}
}
}
}


尝试之后显示Status: Time Limit Exceeded

参阅网上的解法,显然都是用拓扑排序的思想,那么自己的程序速度在哪慢了?

//构造图矩阵
//E[i][j]表示i的前驱是j
for(int i = 0 ; i < prerequisites.length ; i ++){
E[prerequisites[i][0]][prerequisites[i][1]] = 1;
degree[prerequisites[i][1]] ++;
}

//初始化入度
for(int j = 0;j < numCourses; j ++){
for(int i = 0; i < numCourses;i++){
if(E[i][j] == 1){
degree[j] ++;
}
}
}


显然在构造邻接矩阵的时候就可以构造出入度数组,如

for(int i = 0 ; i < prerequisites.length ; i ++){
if(E[prerequisites[i][0]][prerequisites[i][1]] == 0){
E[prerequisites[i][0]][prerequisites[i][1]] = 1;
degree[prerequisites[i][1]] ++;
}

}


需要注意的是:prerequisite[i][j]可能会出现相同的序列对,如{{5,8},{3,5},{1,9},{4,5},{0,2},{1,9},{7,8},{4,9}};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: