leetcode—课程表 拓扑排序

1 题目描述

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

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

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的

2 拓扑排序

有向无环图:一定有一个点的入度为0,如果找不到入度为0的点,这个图一定是带环的

入度:入度就是指向该结点的边数

出度:出度就是该结点指向其他结点的边数

leetcode—课程表 拓扑排序_第1张图片

拓扑排序思路:一个有向无环图,如果图中存在入度为0 的节点,就把这个点删掉,同时也删掉这个点所连的边;重复上述步骤,如果所有点都能被删掉,则这个图可以进行拓扑排序

3 方法

将本题建模成一个求拓扑排序的问题了:

  1. 我们将每一门课看成一个节点;
  2. 如果想要学习课程 AAA 之前必须完成课程 BBB,那么我们从 BBB 到 AAA 连接一条有向边。这样以来,在拓扑排序中,BBB 一定出现在 AAA 的前面。

算法

对于图中的任意一个节点,它在搜索的过程中有三种状态,即:

  • 「未搜索」:我们还没有搜索到这个节点;
  • 「搜索中」:我们搜索过这个节点,但还没有回溯到该节点,即该节点还没有入栈,还有相邻的节点没有搜索完成);
  • 「已完成」:我们搜索过并且回溯过这个节点,即该节点已经入栈,并且所有该节点的相邻节点都出现在栈的更底部的位置,满足拓扑排序的要求。

通过上述的三种状态,我们就可以给出使用深度优先搜索得到拓扑排序的算法流程,在每一轮的搜索搜索开始时,我们任取一个「未搜索」的节点开始进行深度优先搜索。

leetcode—课程表 拓扑排序_第2张图片

4 代码

package matrix;

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

public class TuoPu {
    public static void main(String[] args) {
        int numCourses = 2;
        int[][] prerequisites = {{1,0}};
        System.out.println(canFinish(numCourses, prerequisites));
    }


    // 课程表
    // 存储每个课程的邻接节点列表
    static List> edges;
    // 存储每个课程是否被访问过
    static int[] visited;
    // 表示当前拓扑排序是否合法
    static boolean vaild = true;
    public static boolean canFinish(int numCourses, int[][] prerequisites) {
        // 初始化邻接表和访问状态
        edges = new ArrayList>();
        for(int i = 0; i < numCourses; i++){
            edges.add(new ArrayList());
        }
        visited = new int[numCourses];
        for(int[] info : prerequisites){
            edges.get(info[1]).add(info[0]);
        }

        // 进行拓扑排序
        for(int i = 0; i < numCourses && vaild; i++){
            if(visited[i] == 0){
                dfsHelp(i);  // 调用深度优先搜索进行拓扑排序
            }
        }
        return vaild;
    }

    // 深度优先搜索 用于拓扑排序
    public static void dfsHelp(int u){
        // 标记当前节点为已访问
        visited[u] = 1;
        // 遍历当前节点的所有邻接节点
        for(int v : edges.get(u)){
            // 如果邻接节点未被访问递归
            if(visited[v] == 0){
                dfsHelp(v);
                // 若存在环 则终止搜索
                if(! vaild){
                    return;
                }
            }else if(visited[v] == 1){
                // 若邻接节点正在被访问 说明存在环 终止访问
                vaild = false;
                return;
            }
        }
        // 标记当前节点已经被完全访问
        visited[u] = 2;
    }

}

参考:

作者:力扣官方题解
链接:https://leetcode.cn/problems/course-schedule/solutions/359392/ke-cheng-biao-by-

你可能感兴趣的:(leetcode,算法,职场和发展,java)