拓扑排序——课程表

概述

拓扑排序是一种线性排序,它用于对有向无环图(DAG,Directed Acyclic Graph)中的节点进行排序,使得对于每一条有向边(u, v),节点u在节点v之前。拓扑排序的应用场景通常是任务调度、编译依赖解析等。

例子

假设我们有一个课程表,其中一些课程有先修课程的要求。我们需要按照一个合理的顺序安排这些课程,确保每门课程的先修课程在它之前被学习。

问题描述

给定课程数目numCourses和一个数组prerequisites,其中prerequisites[i] = [a, b]表示要学习课程a,你必须先学习课程b。返回一个可以完成所有课程的顺序。如果存在循环依赖,返回空数组。

解决方案

  • 图的表示:

    • 使用邻接表表示课程依赖关系。
    • 使用一个入度数组inDegree来记录每个节点的入度。
  • 初始化:

    • 遍历prerequisites,构建邻接表adjList和入度数组inDegree
  • 找到入度为0的节点:

    • 将所有入度为0的节点加入队列,这些节点可以作为课程学习的起点。
  • BFS遍历图:

    • 逐个从队列中取出节点,将其加入结果列表。
    • 对于当前节点的每一个邻居节点,将其入度减1,如果入度变为0,则将该节点加入队列。
  • 检查循环依赖:

    • 如果遍历完成后,结果列表中的节点数量不等于numCourses,则存在循环依赖,返回空数组。

代码

import java.util.*;

public class CourseSchedule {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        List<List<Integer>> adjList = new ArrayList<>();
        int[] inDegree = new int[numCourses];
        for (int i = 0; i < numCourses; i++) {
            adjList.add(new ArrayList<>());
        }
        
        // 构建图和入度数组
        for (int[] prerequisite : prerequisites) {
            int course = prerequisite[0];
            int preCourse = prerequisite[1];
            adjList.get(preCourse).add(course);
            inDegree[course]++;
        }
        
        // 找到所有入度为0的节点
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        int[] order = new int[numCourses];
        int index = 0;
        
        // BFS遍历
        while (!queue.isEmpty()) {
            int current = queue.poll();
            order[index++] = current;
            
            for (int neighbor : adjList.get(current)) {
                inDegree[neighbor]--;
                if (inDegree[neighbor] == 0) {
                    queue.offer(neighbor);
                }
            }
        }
        
        // 如果结果中的课程数量不等于numCourses,说明存在循环依赖
        if (index != numCourses) {
            return new int[0];
        }
        
        return order;
    }

    public static void main(String[] args) {
        CourseSchedule cs = new CourseSchedule();
        int numCourses = 4;
        int[][] prerequisites = {{1, 0}, {2, 0}, {3, 1}, {3, 2}};
        
        int[] order = cs.findOrder(numCourses, prerequisites);
        System.out.println(Arrays.toString(order));
    }
}

总结

拓扑排序通过将节点按依赖关系进行排序,有效地解决了类似课程表的依赖问题。通过BFS(或DFS)的方式,我们可以找到一个合法的课程学习顺序,确保所有先修课程都能在相关课程之前完成。如果存在循环依赖,则无法找到有效的学习顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值