210-Course Schedule II

本文介绍了一种解决课程排序问题的有效算法,使用BFS和DFS方法确定合理的课程学习顺序,确保所有课程能够顺利完成。

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

Description

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, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.


Example 1:

Input: 2, [[1,0]] 
Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished   
             course 0. So the correct course order is [0,1] .

Example 2:

Input: 4, [[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both     
             courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. 
             So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3] .

Note:

  1. The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
  2. You may assume that there are no duplicate edges in the input prerequisites.

问题描述

你总共需要上n节课, 这些课被标记为0到n - 1

一些课会有前置课程, 例如上0号课之前需要先上1号课, 这种关系通过前置对来表示: [0, 1]

给定课程数目, 以及前置对列表, 返回课程顺序, 使得你可以上完所有课程

可能会有多个正确顺序, 你只需要返回其中一个。如果不可能完成所有课程, 返回空数组


问题分析

BFS和DFS的大致思路

BFS:先处理入度为零的课程, 将其后置课程入度减一, 处理新的入度为0的课程, 依次类推
DFS : 先处理出度为零的课程


解法1

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if(numCourses == 0) return new int[0];
        //课程的入度数组
        int[] ingrade = new int[numCourses];
        //图, 以[a, b]中的b作为数组下标, 列表存储后置课程
        List<Integer>[] graph = new List[numCourses];
        for(int i = 0;i < numCourses;i++)   graph[i] = new ArrayList();
        //构建入度数组和图
        for(int[] edge : prerequisites){
            ingrade[edge[0]]++;
            graph[edge[1]].add(edge[0]);
        }

        List<Integer> res = new ArrayList();
        Queue<Integer> queue = new LinkedList();
        //将入度为0的课程放入初始队列
        for(int i = 0;i < numCourses;i++){
            if(ingrade[i] == 0) queue.add(i);
        }
        //BFS
        while(!queue.isEmpty()){
            int node = queue.poll();
            res.add(node);
            List<Integer> edges = graph[node];
            for(int i : edges){
                ingrade[i]--;
                if(ingrade[i] == 0) queue.add(i);
            }
        }

        if(res.size() != numCourses)    return new int[0];

        int[] result = new int[numCourses];
        for(int i = 0;i < numCourses;i++)   result[i] = res.get(i);

        return result;
    }
}

解法2

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Solution{

    public int[] findOrder(final int n, int[][] prereq){
        //图
        ArrayList<Integer>[] adj = (ArrayList<Integer>[]) (new ArrayList[n]);
        //初始化图
        for(int i = 0; i < n; i++) adj[i] = new ArrayList();
        for(int[] edge : prereq)   adj[edge[1]].add(edge[0]);
        //两个数组的作用综合起来, 判断是否存在回路, d用来标记已经处理过的节点, 可以提高效率
        //可以想象一下, 若只有d的话, 不能处理在不同的递归过程(因为是迭代的过程中进行递归)中遇到已经处理过的课程
        //而有了f, 当遇到已经处理的课程, 若该课程标记为true, 说明是在之前的递归过程中处理过的课程, 因此不会存在回路
        //若为false, 说明之前的递归过程未处理此课程, 本次递归过程存在回路
        boolean[] d = new boolean[n];
        boolean[] f = new boolean[n];
        ArrayList<Integer> order = new ArrayList();

        //注意这里利用d数组来提高效率
        for(int i = 0; i < n; i++){
            if(!d[i] && !dfs(adj, d, f, i, order))  return new int[0];
        }
        //注意这里, 需要逆序
        Collections.reverse(order);
        int[] result = new int[n];
        for(int i = 0; i < n; i++) result[i] = order.get(i);

        return result;
    }

    public boolean dfs(ArrayList<Integer>[] adj, boolean[] d, boolean[] f, int s, List<Integer> order){
        //注意这里的处理
        if(d[s])   return f[s];
        //注意d和f的变化
        d[s] = true;
        for(int a : adj[s]){
            if(!dfs(adj, d, f, a, order))  return false;
        }
        f[s] = true;
        //可以看出, 出度为0的最先处理
        order.add(s);

        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值