LeetCode -- Course Schedule

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}};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值