拓扑排序(Kahn算法)

拓扑排序(Kahn算法)

什么是拓扑排序

拓扑排序是对 有向无环图(DAG, Directed Acyclic Graph) 的顶点进行的一种线性排序,使得对于每一条有向边 (u, v),顶点 u 在排序结果中都出现在顶点 v 的前面。

核心前提:图必须是 DAG(无环),若图中存在环,则无法进行拓扑排序。

应用场景

拓扑排序主要用于处理依赖关系的场景,典型例子包括:

  • 课程安排:大学选课系统中,某些课程有先修课要求(如 "数据结构" 必须在 "算法分析" 之前学习),拓扑排序可给出合理的选课顺序。
  • 任务调度:项目管理中,多个任务存在依赖关系(如 "设计" 完成后才能 "开发"),拓扑排序可生成任务执行序列。
  • 编译顺序:编译器处理文件依赖时(如 .h 头文件被 .cpp 源文件依赖),拓扑排序可确定文件的编译顺序。

三、Kahn 算法原理与步骤

Kahn 算法是一种基于入度的贪心算法,核心思想是 "不断处理入度为 0 的顶点"。

原理

  • 入度定义:顶点的入度是指指向该顶点的有向边数量。
  • 贪心策略:每次选择入度为 0 的顶点(无前置依赖),将其加入拓扑序列,然后 "移除" 该顶点的所有出边(即减少邻接顶点的入度)。
    环检测:若最终拓扑序列的顶点数小于图的总顶点数,说明图中存在环。

步骤

  • 初始化:
    计算图中所有顶点的入度,存入 inDegree 数组。
    使用一个队列(或栈)存储所有入度为 0 的顶点。
  • 循环处理:
    从队列中取出一个顶点 u,加入拓扑序列。
    遍历 u 的所有邻接顶点 v,将 v 的入度减 1。
    若 v 的入度变为 0,将其加入队列。
  • 终止条件:
    队列为空时,检查拓扑序列长度:
    若长度等于顶点数:输出拓扑序列。
    若长度小于顶点数:图中存在环,无法拓扑排序。
java 复制代码
import java.util.*;

public class TopologicalSortKahn {

    // 拓扑排序函数,返回拓扑序列(若有环则返回空列表)
    public static List<Integer> topologicalSort(int numVertices, List<List<Integer>> adj) {
        int[] inDegree = new int[numVertices]; // 存储每个顶点的入度
        List<Integer> topoOrder = new ArrayList<>(); // 存储拓扑序列

        // 1. 计算所有顶点的入度
        for (int u = 0; u < numVertices; u++) {
            for (int v : adj.get(u)) {
                inDegree[v]++; // 边 u->v,v 的入度+1
            }
        }

        // 2. 初始化队列,加入所有入度为 0 的顶点
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numVertices; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }

        // 3. 循环处理队列中的顶点
        while (!queue.isEmpty()) {
            int u = queue.poll(); // 取出入度为 0 的顶点 u
            topoOrder.add(u);     // 加入拓扑序列

            // 遍历 u 的所有邻接顶点 v
            for (int v : adj.get(u)) {
                inDegree[v]--; // 移除边 u->v,v 的入度-1
                if (inDegree[v] == 0) {
                    queue.offer(v); // 若 v 入度为 0,加入队列
                }
            }
        }

        // 4. 检测是否有环:若拓扑序列长度不等于顶点数,说明有环
        if (topoOrder.size() != numVertices) {
            return new ArrayList<>(); // 有环,返回空列表
        }

        return topoOrder;
    }

    // 测试主函数
    public static void main(String[] args) {
        // 示例:构建一个 DAG(顶点 0~5)
        int numVertices = 6;
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < numVertices; i++) {
            adj.add(new ArrayList<>());
        }

        // 添加边:u->v(u 是 v 的前置依赖)
        adj.get(0).add(1); // 0->1
        adj.get(0).add(2); // 0->2
        adj.get(1).add(3); // 1->3
        adj.get(2).add(3); // 2->3
        adj.get(3).add(4); // 3->4
        adj.get(3).add(5); // 3->5

        // 执行拓扑排序
        List<Integer> result = topologicalSort(numVertices, adj);

        // 输出结果
        if (result.isEmpty()) {
            System.out.println("图中存在环,无法拓扑排序!");
        } else {
            System.out.println("拓扑排序结果:" + result);
        }
    }
}
相关推荐
Hello!!!!!!2 小时前
C++基础(六)——数组与字符串
c++·算法
山半仙xs2 小时前
基于卡尔曼滤波的人脸跟踪
人工智能·python·算法·计算机视觉
智者知已应修善业2 小时前
【51单片机调用__TIME__无法实时时间】2023-7-10
c++·经验分享·笔记·算法·51单片机
做时间的朋友。2 小时前
算法-最大单入口空闲区域
算法
千寻girling2 小时前
机器学习 | 逻辑回归 | 尚硅谷学习
java·人工智能·python·学习·算法·机器学习·逻辑回归
Shadow(⊙o⊙)3 小时前
C++常见错误解析2.0
开发语言·数据结构·c++·后端·学习·算法
永远不会的CC3 小时前
研0上岸找实习面试经历
python·算法·面试
帅小伙―苏3 小时前
力扣483找到字符串中所有字母异位词
算法·leetcode
小O的算法实验室3 小时前
2022年IEEE TETCI,基于矩阵的进化计算,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进