leetcode_207_课程表_拓扑排序

本文探讨了在给定课程总数及先决条件的情况下,如何判断所有课程是否可按要求完成。通过拓扑排序的方法,检查有向图中是否存在环,以此判断课程安排的可行性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

老规矩上题目2333:

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-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。这是不可能的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


思路:

很明显的拓扑排序。也就是说,如果这个有向图可以满足拓扑排序的要求,则一定可以满足题目的要求。

而拓扑排序的要求:没有环。

所以说到底就是要检查该有向图中是否有环。

一般检查有无环,使用DFS或者BFS解决即可。(自己在实现时使用BFS)

在使用BFS时,利用一个queue来保存下一次要遍历的点。

根据拓扑的规则:对于点A,当且仅当点A的入度为0时才可以对其进行遍历。

所以各点入队的判断标准为:入度为零。

所以,必须要在执行BFS之前将各点的入度记录下来:

int []enterd = new int[numCourses];

        //记录每个点的入度
        for(int i = 0;i < n;i++)
            enterd[prerequisites[i][0]]++;

然后再进行bfs:

while(!queue.isEmpty()){
            int sp = queue.removeFirst();
//            enterd[sp]--;
            count++;  //遍历的点统计
            for(int i = 0;i < n;i++){
                if(prerequisites[i][1] != sp) continue;  //此边的起点不是当前的点时可以直接continue。
                enterd[prerequisites[i][0]]--;   //此时的边i一定是以sp为起点的边,所以将终点的入度减一
                if(enterd[prerequisites[i][0]] == 0)  //如果入度以及减为0,则满足入队条件,可以入队。
                    queue.addLast(prerequisites[i][0]);   
            }

        }

实现:

java版:

package leetcode;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/*
USER:LQY
DATE:2020/8/4
TIME:8:20
*/
public class leetcode_207 {

    public static void main(String []args){
        int n = 2;
        int [][] pre = {
//                {1,0}
//                {0,1}
//                {1,0},{2,6},{1,7},{5,1},{6,4},{7,0},{0,5}
                {1,0},{0,3},{0,2},{3,2},{2,5},{4,5},{5,6},{2,4}
        };
        System.out.println(new leetcode_207().canFinish1(7, pre));
    }
    public boolean canFinish1(int numCourses, int[][] prerequisites) {
        int n = prerequisites.length;
        int []enterd = new int[numCourses];

        //记录每个点的入度
        for(int i = 0;i < n;i++)
            enterd[prerequisites[i][0]]++;
        //将入度为0的点加入队列
        LinkedList<Integer> queue = new LinkedList<>();
        for(int i = 0;i < numCourses;i++){
            if(enterd[i] == 0) queue.add(i);
        }
        //bfs
        int count = 0;
        while(!queue.isEmpty()){
            int sp = queue.removeFirst();
//            enterd[sp]--;
            count++;  //遍历的点统计
            for(int i = 0;i < n;i++){
                if(prerequisites[i][1] != sp) continue;  //此边的起点不是当前的点时可以直接continue。
                enterd[prerequisites[i][0]]--;   //此时的边i一定是以sp为起点的边,所以将终点的入度减一
                if(enterd[prerequisites[i][0]] == 0)  //如果入度以及减为0,则满足入队条件,可以入队。
                    queue.addLast(prerequisites[i][0]);
            }

        }
        //如果统计的遍历过的节点数与想要学习的课程数目一样则返回true
        return count==numCourses;


    }
//    int n;
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int n = prerequisites.length;

        int []dot = new int[numCourses];
        int []book = new int[numCourses];

        LinkedList<Integer> queue = new LinkedList<>();
        LinkedList<Integer> looked = new LinkedList<>();
        //找出入度为0的点
        for(int i = 0;i < n;i++){
            dot[prerequisites[i][0]]++;

        }
        //如果没有入度为0的点,false
//        int f = 1;
//        for(int i = 0;i < numCourses;i++){
//            if(dot[i] == 0){
//                f = 0;
//                System.out.println("点"+i+"的入度为0");
//            }
//
//        }

//        if(f == 1) return false;

        for(int i = 0;i < numCourses;i++){
            if(dot[i] != 0) continue;
//            if(looked.contains(i)) continue;
            //bfs
            queue.clear();
            Arrays.fill(book, 0);
            queue.add(i);
//            book[i] = i + 1;
            while(!queue.isEmpty()){
                int sp = queue.removeFirst();

                for(int j = 0;j < n;j++){

                    if(prerequisites[j][1] != sp) continue;
                    if(book[prerequisites[j][0]]==2 && book[sp]==2) return false;
                    System.out.println(sp+"->"+prerequisites[j][0]);
                    queue.addLast(prerequisites[j][0]);
                }
                //推迟到现在标记
                if(!looked.contains(sp)) looked.add(sp);
                book[sp]++;
            }

        }
        System.out.println("走过:"+looked.size());
        for(int i : looked){
            System.out.print(i+" ");
        }
        if(looked.size() != numCourses) return false;
        //最后判断:如果有哪一个book[i] == 2,则说明有环。

        return true;
    }

}

Python版:

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        n = len(prerequisites)

        enterd = [0 for _ in range(numCourses)]

        for i in range(n):
            enterd[prerequisites[i][0]] += 1
        que = []
        for i in range(numCourses):
            if enterd[i] == 0:
                que.append(i)
        
        while(len(que) > 0):
            sp = que[0]
            que = que[1:]
            numCourses -= 1
            for i in range(n):
                if prerequisites[i][1] != sp:
                    continue
                np = prerequisites[i][0]
                enterd[np] -= 1
                if enterd[np] == 0:
                    que.append(np)
        
        return numCourses == 0

C++版:

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int n = prerequisites.size();

        int enterd[numCourses];
        for(int i = 0;i < numCourses;i++)
            enterd[i] = 0;
        for(int i = 0;i < n;i++){
            enterd[prerequisites[i][0]]++;
        }
        queue<int> que;
        for(int i = 0;i < numCourses;i++)
            if(enterd[i] == 0) que.push(i);

        while(!que.empty()){
            int sp = que.front();
            que.pop();
            numCourses--;
            for(int i = 0;i < n;i++){
                if(prerequisites[i][1] != sp) continue;
                int np = prerequisites[i][0];
                enterd[np]--;
                if(enterd[np] == 0) que.push(np);
            }
        }

        return numCourses == 0;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值