拓扑排序 topologicalSort

2023-11-02

背景知识 ---- 图 (Graph)

顶点和边 (vertex and edge)

下图中的数字为图的顶点,数字之间的线为图的边
在这里插入图片描述

无向图 (Undirected Graph)

下图中顶点和顶点之间是没有方向性的,可以双向通行。
也就是同时存在 (1, 2) 和 (2, 1)
在这里插入图片描述

有向图 (Directed Graph)

下图是有方向性的,只能往箭头的方向通行
也就是只存在 (1, 2), 不存在 (2, 1)
在这里插入图片描述

有向图的degree

  • 对于有向图,每个顶点都有一个degree,并且分为
  • in-degree: number of edgs into vertex a
  • out-degree: number of edges out of vertex a
  • 下图中
    1: in-degree = 0, out-degree = 1
    2: in-degree = 2, out-degree = 1
    3: in-degree = 0, out-degree = 2
    4: in-degree = 2, out-degree = 0
    在这里插入图片描述

图中的环

图中可以形成回路的是环
下图中 2 —> 4 ----> 3 —> 2,就形成了环在这里插入图片描述

基本概念

什么是拓扑排序

  • 对于一个有向无环图,将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前
  • 如下图: 2 永远不可能在 1和3之前出现,4 用于不可能在1,2,3之前出现
  • 拓扑排序不是传统的排序算法,一个图可能有多种拓扑排序

下图的拓扑排序是: 1,3,2,4 或者 3,1,2,4,有两种拓扑排序
在这里插入图片描述

拓扑排序的代码思路 — 利用广度优先搜索(BFS)

  1. 统计每个点的 in-degree
  2. 将每个in-degree为0的点放进队列 (Queue),作为初始值
  3. 不断的从队列里拿出一个点 (pop操作), 将这个点的所有邻居的indegree减1
  4. 如果发现有新的indegree为0的点,则放入队列
  5. 重复3,4步,直到队列为空

Example:
Step 1: 初始化队列:
Queue: {1,3} 因为1和3的indegree为0
Output: { };
在这里插入图片描述

Step 2:队首元素出队放入output, 更新2的indegree为 1
Queue: {3}
Output: {1};
在这里插入图片描述

Step 3: 队首元素出队放入output, 更新2的indegree为 0, 同时将 2 放进 Queue
Queue: {2}
Output: {1, 3};
在这里插入图片描述

Step 4: 队首元素出队放入output, 更新4的indegree为 0, 同时将 4 放进 Queue
Queue: {4}
Output: {1, 3, 2};
在这里插入图片描述

Step 3: 队首元素出队放入output, Queue为空,结束顺换,排序完成
Queue: {}
Output: {1, 3, 2, 4};

拓扑排序的实现

/**
 * Definition for Directed graph.
 * class DirectedGraphNode {
 *     int label;
 *     ArrayList<DirectedGraphNode> neighbors;
 *     DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); }
 * };
 */

public class Solution {
    /*
     * @param graph: A list of Directed graph node
     * @return: Any topological order for the given graph.
     */
    public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
    
        ArrayList<DirectedGraphNode> outputList = new ArrayList<DirectedGraphNode>();
        //用于储存每个点的inderee
        HashMap<DirectedGraphNode, Integer> indegree = new HashMap<>();
        //找出每个node的indegree
        for (DirectedGraphNode node: graph) {
            for (DirectedGraphNode neighbor: node.neighbors) {
                if (indegree.get(neighbor) != null) {
                    indegree.put(neighbor, indegree.get(neighbor) + 1);
                }
                else {
                    indegree.put(neighbor, 1);
                }
            }
        }
        
        //将indegree为0的点放进Queue
        List<DirectedGraphNode> queue = new ArrayList<DirectedGraphNode>();
        for (DirectedGraphNode node: graph) {
            if (indegree.get(node) == null) {
                queue.add(node);
            }
        }
        
        //BFS搜索,不断的出队列,同时更新每个点的indegree
        //如果遇到indegree为0的节点就加入队列
        while (!queue.isEmpty()) {
            DirectedGraphNode tempNode = queue.get(0);
            queue.remove(0);
            outputList.add(tempNode);
            
            for (DirectedGraphNode neighbor: tempNode.neighbors) {
                //对neighbor的indegree减1
                indegree.put(neighbor, indegree.get(neighbor)-1);
                if (indegree.get(neighbor) == 0) {
                    queue.add(neighbor);
                }
            }
        }
        
        return outputList;
    }
}

时间复杂度

拓扑排序的时间复杂度为 O(V + E) , 也就是在拓扑排序中每个点被遍历一次,每个边被遍历一次

拓扑排序的相关问题

判断是否存在拓扑排序

  • 当最后被排序的顶点数量小于图中的总顶点数时,就说明图中存在环,所以没有拓扑排序
    当下图中的1 出队之后,就没有indegree为0的顶点了,所以队列为空循环结束
    此时被排序的顶点只有 1个,小于总顶点数 3
    所以不存在拓扑排序
    在这里插入图片描述

判断是否仅存一个拓扑排序

  • 如果队列中始终只有一个元素,则说明只存在一个拓扑排序

如何求按最小或最大顺序排列拓扑排序

  • 用Heap代替Queue,就可以每次pop出最大或最小元素

Leetcode Example:

在这里插入图片描述

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        //储存所有的点对应的进阶课程,放进hash
        Map<Integer, List<Integer>> graph = new HashMap<>(); //Key: course, value: next-level courses
        //储存所有点的indegree
        Map<Integer, Integer> indegree = new HashMap<>(); //Key: courses, Value: indegree

        //初始化两个hashtable
        for (int i = 0; i < numCourses; i++) {
            graph.put(i, new ArrayList<Integer>());
            indegree.put(i, 0);
        }

        //记录每个点的indegree和neighbors
        for (int i = 0; i < prerequisites.length; i++) {
            indegree.put(prerequisites[i][0], indegree.get(prerequisites[i][0])+ 1);
            graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }

        //进行拓扑排序
        List<Integer> queue = new ArrayList<>();
        int [] output = new int[numCourses];
        int totalCourse = 0;
        //找出indegree为0的course,放进queue
        for(Map.Entry<Integer, Integer> entry: indegree.entrySet()) {
           if (entry.getValue() == 0) {
               queue.add(entry.getKey());
           }
        }

        while (!queue.isEmpty()) {
           int course = queue.get(0);
           queue.remove(0);
           output[totalCourse] = course;
           totalCourse++;
           List<Integer> neighbors = graph.get(course);
           //遍历所course的所有neighbor
           for (int i = 0; i < neighbors.size(); i++) {
               int neighbor = neighbors.get(i);
               //将course的neighbor的indegree减一
               indegree.put(neighbor, indegree.get(neighbor) - 1);
               //如果有neighbor的indegree变为0,加入queue
               if (indegree.get(neighbor) == 0) {
                   queue.add(neighbor);
               }
           }
        }
        
        //如果最后排序的数量小于总数量,则说明有环的存在, 返回false
        if (totalCourse < numCourses) {
            return new int[0];
        }

        return output;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

拓扑排序 topologicalSort 的相关文章

随机推荐

  • ARM MMU 详解

    一 MMU 的产生 许多年以前 当人们还在使用DOS或是更古老的操作系统的时候 计算机的内存还非常小 一般都是以 K 为单位进行计算 相应的 当时的程序规模也不大 所以内存容量虽然小 但还是可以容纳当时的程序 但随着图形界面的兴起 还有用户
  • @Resource注解的使用

    1 在spring的配置文件中导入命名空间 xmlns context http www springframework org schema context http www springframework org schema cont
  • 推荐一个好组件Javascript文本比较工具

    您的项目上有没有遇到需要在前端显示并比较两个不同版本的文本文件 希望它像winmerge 或eclipse的svn比较工具那样标注不同的地方 我找到了 分享给大家吧 最近项目上需要一个类似cvs svn文本比较工具 把左右两个文本中不一样的
  • 在Eclipse添加Android兼容包( v4、v7 appcompat )

    昨天添加Android兼容包 碰到了很多问题 在这里记录一下 让后面的路好走 如何选择兼容包 请参考Android Support Library Features 二 一 下载Support Library 方法1 右击项目 选择Andr
  • Jenkins配置定时调度部署

    H 22 表示每天22点 自动构建
  • 怎么找CVPR/ICCV/ECCV文章

    原文链接 https www jianshu com p aed3dd8c81fa CVPR论文查找 每年一届 点击如下链接 输入相关关键字即可搜索 http openaccess thecvf com CVPR2013 search py
  • 冒泡排序原理详解及代码实现

    1 冒泡排序数组排序常用的一种方式 为什么要叫冒泡排序呢 这还要从它的原理说起 2 代码实现 低效版 3 原理详解 冒泡排序最基本的思想就是从左到右依次判断相邻的两个数的大小关系 如果前面的数大于后面的数 则两数互换位置 但是只是从左到右遍
  • C++11---智能指针

    智能指针 1 为什么引入智能指针 1 1 内存泄漏 2 智能指针的使用及原理 2 1 RAII 2 2 智能指针的原理 3 C 98 auto ptr 4 unique ptr 5 shared ptr 5 1 循环引用 6 weak pt
  • 在Anaconda下安装了TensorFlow库matplotlib库却调用不了了

    在Anaconda下安装了TensorFlow库 但是Anaconda中的matplotlib库却调用不了了 解决方法如下 1 打开Anaconda Prompt 2 输入activate tensorflow 3 输入conda inst
  • 2022年全国职业院校技能大赛(中职组)网络安全竞赛试题解析-A

    2022年全国职业院校技能大赛 中职组 网络安全竞赛试题 1 总分100分 需要环境可以私信博主 赛题说明 一 竞赛项目简介 网络安全 竞赛共分A 基础设施设置与安全加固 B 网络安全事件响应 数字取证调查和应用安全 C CTF夺旗 攻击
  • StackGAN笔记

    Stack可译做堆叠 就是在GAN上面再放上一个GAN 作者讲述的自己的解决思路 原来难以生成高分辨率的图像 他们分解了这个问题 把生成高分辨率图片这个任务分解成两个更为简单的任务 就是文中说的一个GAN生成大致的形状和颜色 第二个GAN生
  • 自主HttpServer实现(C++实战项目)

    文章目录 项目介绍 CGI技术 概念 原理 设计框架 日志文件 TCPServer 任务类 初始化与启动HttpServer HTTP请求结构 HTTP响应结构 线程回调 EndPoint类 EndPoint主体框架 读取HTTP请求 处理
  • tidymodels-workflow工作流

    在阅读这篇文章前 我强烈建议你先读一下tidymodels入门篇 tidymodels用于机器学习的细节 首先对tidymodels有一个整体的认知 今天主要介绍workflow的用法 workflow可以把你的数据预处理步骤和模型连接起来
  • 深聊性能测试,从入门到放弃之:性能测试基准与阶段

    如何做性能测试 1 引言 2 性能测试内容 2 1 基准测试 2 2 日常压力测试 2 3 峰值压力测试 2 4 容量测试 2 5 稳定性测试 3 性能测试阶段 3 1 测试确认 3 2 确定通过标准 3 3 测试设计 3 4 测试环境准备
  • 【问题解决】后端如何以文件流的形式返回本地资源给前端,提供下载服务

    后端以文件流的形式返回本地资源 文件地址 String path PDFpath File file new File PDFpath 读取生成的PDF文件 InputStream inputStream OutputStream outp
  • python中文主客观分类

    查了很久发现主客观分析的方法很多 但是数据集少的可怜 能直接使用的库更少 好不容易找到一个 收藏一下 Github 页面 https github com liuhuanyong ZhuguanDetection 下载与使用方法 git c
  • ELK之logstash单节点安装

    ELK之logstash单节点安装 最近在搞ELK 写个文章记录分享一下经验 去官网上下载对应版本的logstash安装包 将工具包上传至服务器 1 解压工具包 命令 tar xzvf logstash tar gz 2 配置logstas
  • Pycharm全局搜索窗口关不掉的解决方案 (ctrl shift F)

    Pycharm全局搜索窗口可以ctrl shift F打开 但是找不到直接关闭的按钮 清空搜索内容也关不掉 只能把窗口挪走 再次ctrl shift F也不会打开新的窗口 一度有点烦恼 后来发现 按ESC就行了 按ESC就行了 按ESC就行
  • JAVA基础常见简答题面试题

    1 为什么java是半编译半解释性的语言 java如何实现跨平台 java的编译器先将其编译为class文件 也就是字节码 然后将字节码交由jvm java虚拟机 解释执行 所以很多地方都说 java是一种半编译 半解释执行 的语言 JAV
  • 拓扑排序 topologicalSort

    拓扑排序 topologicalSort 背景知识 图 Graph 顶点和边 vertex and edge 无向图 Undirected Graph 有向图 Directed Graph 有向图的degree 图中的环 基本概念 什么是拓