hot100-课程表

课程表

题目描述:

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

思路:

在做这个题之前,我们需要先了解一下拓扑排序。

拓扑排序是一个有向无环图所有顶点的序列,满足每个顶点只出现一次,且如果存在A到B得路径,那么序列中顶点A需要出现在顶点B得前面。

课程表也类似于这种情况,我们要想可以完成所有课程,需要在prerequisites构成的图中尝试找是否存在拓扑排序(比如相对于[0,1],我们要让1指向0,这样如果有拓扑排序,1一定在0得左边),如果存在说明可以刚好修完课程并满足先修课程在各自的课程之前修完。

尝试找拓扑排序的方法1为:

从入度为0得节点出发,将其先存入序列中,然后将这些节点指向的节点的入度减一,再找入度为0得节点,这样重复直至没有入度为0得节点,如果图存在拓扑排序,那么最后序列中节点数一定等于总结点数,且不存在入度不为0得节点。

那么我们就可以尝试找prerequisites构成的图中的拓扑排序,如果最后满足序列中节点数一定等于总结点数,且不存在入度不为0得节点,就说明图是无环的,那就可以修完所有课程

深度优先:

不能修完所有课程的条件是,存在课A和课B,课A修之前需要修课B,但是课B修之前也需要修课A,即prerequisites构成的图中包含环,那么我们只需要从每个节点出发看有没有环,若找到环,说明无法正常修完课程。

代码:

拓扑排序:

public boolean canFinish(int numCourses, int[][] prerequisites) {

    //拓扑排序
    if(numCourses<=0)return false;
    if(prerequisites.length==0)return true;
    int[] in = new int[numCourses];
    int res = 0;
    List<List<Integer>> list = new ArrayList<>();
    for (int i = 0; i < numCourses; i++) {
        list.add(new ArrayList<>());
    }
    for (int i = 0; i < prerequisites.length; i++) {
        in[prerequisites[i][0]]++;
        list.get(prerequisites[i][1]).add(prerequisites[i][0]);
    }
    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < in.length; i++) {
        if(in[i]==0)queue.add(i);

    }
    while(!queue.isEmpty()){
        int tmp = queue.poll();
        res++;
        for (int i = 0; i < list.get(tmp).size(); i++) {
            in[list.get(tmp).get(i)]--;
            if(in[list.get(tmp).get(i)]==0) {
                queue.add(list.get(tmp).get(i));
            }
        }
    }
    return res==numCourses;

}

深度优先

public boolean canFinish2(int numCourses, int[][] prerequisites){
    int[] status = new int[numCourses];
    List<List<Integer>> list = new ArrayList<>();
    for (int i = 0; i < numCourses; i++) {
        list.add(new ArrayList<>());
    }
    for (int i = 0; i < prerequisites.length; i++) {
        list.get(prerequisites[i][1]).add(prerequisites[i][0]);
    }
    for (int i = 0; i < numCourses; i++) {
        if(!dfs(list,i,status))return false;
    }
    return true;
}

public static boolean dfs(List<List<Integer>> list,int i,int[] status){
    if(status[i]==1)return false;
    if(status[i]==-1)return true;
    status[i]=1;
    for (int j = 0; j < list.get(i).size(); j++) {
        if(!dfs(list,list.get(i).get(j),status))return false;
    }
    status[i]=-1;
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值