17.4:图的拓扑排序

news2024/11/15 11:48:25

拓扑序一定是有向无环图。

拓扑排序不唯一

在这里插入图片描述
在这里插入图片描述

拓扑排序方法一:

利用入度为零进行排序

思路:谁的入度为0,谁就是开始节点,将开始节点打印完之后,将开始节点的直接邻居的入度减1,然后重复。

在这里插入图片描述

package algorithmbasic.class17;

import java.util.*;

//图的拓扑排序
public class TopologySort {
    public static List<Node> sortedTopology(Graph graph) {
        //result用来盛放排序结果。
        List<Node> result = new ArrayList<>();
        //inmap存放节点与入度的对应值。
        //key ——> 某个节点, value ——> 剩余入度
        HashMap<Node, Integer> inmap = new HashMap<>();
        //只有节点的入度为0才可以进入此队列。
        Queue<Node> inZeroQueue = new LinkedList<>();

        for (Node node : graph.nodes.values()) {
            inmap.put(node, node.in);
            if (node.in == 0) {
                inZeroQueue.add(node);
            }
        }

        Node cur = inZeroQueue.poll();
        result.add(cur);
        for (Node next : cur.nexts) {
            //剩余入度减1.
            inmap.put(next, inmap.get(next) - 1);
            if (inmap.get(next) == 0) {
                inZeroQueue.add(next);
            }
        }
        return result;
    }
}

拓扑排序方法二:

利用点次进行排序。

在这里插入图片描述

点次越大的,排序位置越靠前。

而且我发现可以使用递归进行求点次。我们要求0的点次,那就需要求他的直接邻居1,2,3的点次,然后对邻居的点次求和再加1,就是0的点次。我们可以将每个点的点次放在一个表里面,这个表记录着哪个节点的点次对应着多少。这样我们再求其他节点点次时直接从表里拿就行,减少重复性工作。

思路:
1:创建一个表 HashMap<DirectedGraphNode, Record> order = new HashMap<>() 用来记录每个节点对应的点次是多少。

2:遍历整个图中的每一个节点,记录其点次。

3:创建一个有序表ArrayList records = new ArrayList<>() 用来记录点次。

4:根据点次进行由大到小的排序。records.sort(new MyComparator());

5:然后再创建一个有序表ArrayList dnodes = new ArrayList<>() 根据点次的顺序记录节点。

注意为什么要创建Record这个内部类。因为在 “ 根据点次的顺序记录节点” 时,我们需要根据点次找到对应的节点,使用map是不可以的,因为点次大小有重复的。所以我们采用内部类的方法,这样每个点次都会有一一对应的节点。

package algorithmbasic.class17;
//图的拓扑排序方法二

import java.util.*;

// OJ链接:https://www.lintcode.com/problem/topological-sorting
public class TopologicalOrderDFS2 {
    /**
     * 节点内部类
     */
    // 不要提交这个类
    public static class DirectedGraphNode {
        public int label;
        public ArrayList<DirectedGraphNode> neighbors;

        public DirectedGraphNode(int x) {
            label = x;
            neighbors = new ArrayList<DirectedGraphNode>();
        }
    }
    
    /**
     * 点次内部类。
     */

    //记录某个节点的点次。
    public static class Record {
        public DirectedGraphNode node;
        //点次。
        public long nodes;

        public Record(DirectedGraphNode node, long nodes) {
            this.node = node;
            this.nodes = nodes;
        }
    }

    /**
     * topSort方法
     * 传入一张图的所有节点,返回排序好后的所有节点。
     */

    public static ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        //采用计算每个节点点次的方法。
        //建立一张表用来记录每个节点对应的点次是多少。
        HashMap<DirectedGraphNode, Record> order = new HashMap<>();
        ArrayList<Record> records = new ArrayList<>();
        ArrayList<DirectedGraphNode> dnodes = new ArrayList<>();

        //遍历图中每个节点,并记录每个节点出现的点次。
        for (DirectedGraphNode cur : graph) {
            Record record = process(cur, order);
            records.add(record);
            //order.put(cur, record);
        }
        //Arrays.sort(records,new MyComparator());
        records.sort(new MyComparator());
        for (Record r : records) {
            dnodes.add(r.node);//这就是为什么要建立Record这个内部类的原因。
        }
        return dnodes;
    }
    
    /**
     * 求点次的方法。
     */

    //传入一个节点返回这个节点的点次。在递归的过程当中每个节点的点次其实都已经记录好了。
    public static Record process(DirectedGraphNode node, HashMap<DirectedGraphNode, Record> order) {
        if (order.containsKey(node)) {
            return order.get(node);
        }
        //order中没有该点。
        long count = 0;
        for (DirectedGraphNode n : node.neighbors) {
            Record r = process(n, order);
            count += r.nodes;
        }
        Record record = new Record(node, count + 1);
        //先把当前节点及其点次放入map中然后再返回。这样我们再求其他节点点次时直接从表里拿就行,减少重复性工作。
        order.put(node, record);
        return record;
    }

    /**
     * 比较器
     */
    
    public static class MyComparator implements Comparator<Record> {
        @Override
        public int compare(Record o1, Record o2) {
            //不要将long类型数据强制转换成int类型,会出现溢出和截断的风险,导致数据出现错误。
            //例如2147483648L -> int:-2147483648
            //它超过了int类型的最大值 2147483647。当将其强制转换为int类型时,结果被截断为int类型的最小值 -2147483648。
            //return (int)(o2.nodes - o1.nodes);
            return o1.nodes == o2.nodes ? 0 : (o1.nodes > o2.nodes ? -1 : 1);
        }
    }
}

拓扑排序方法三:

根据深度进行排序

在这里插入图片描述

package algorithmbasic.class17;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;

public class TopologicalOrderDFS1 {

    /**
     * 节点内部类
     */

    public static class DirectedGraphNode {
        public int label;
        public ArrayList<DirectedGraphNode> neighbors;

        public DirectedGraphNode(int x) {
            label = x;
            neighbors = new ArrayList<DirectedGraphNode>();
        }
    }

    /**
     * 深度内部类。
     */

    public static class Deep {
        public long deep;
        public DirectedGraphNode node;

        public Deep(long deep, DirectedGraphNode node) {
            this.deep = deep;
            this.node = node;
        }
    }

    /**
     * topSort方法
     */

    public static ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        HashMap<DirectedGraphNode, Deep> order = new HashMap<>();
        ArrayList<Deep> deeps = new ArrayList<>();
        ArrayList<DirectedGraphNode> dNodes = new ArrayList<>();

        for (DirectedGraphNode node : graph) {
            Deep d = process(node, order);
            deeps.add(d);
        }
        deeps.sort(new MyComparator());
        for (Deep d : deeps) {
            dNodes.add(d.node);
        }
        return dNodes;
    }


    public static Deep process(DirectedGraphNode node, HashMap<DirectedGraphNode, Deep> order) {
        if (order.containsKey(node)) {
            return order.get(node);
        }
        long max = Long.MIN_VALUE;
        for (DirectedGraphNode n : node.neighbors) {
            Deep d = process(n, order);
            max = Math.max(max, d.deep);
        }
        Deep deep = new Deep(max + 1, node);
        order.put(node, deep);
        return deep;
    }

    public static class MyComparator implements Comparator<Deep> {
        @Override
        public int compare(Deep o1, Deep o2) {
            return o1.deep == o2.deep ? 0 : (o1.deep > o2.deep ? -1 : 1);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/605710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软件测试】稳定性和可靠性测试在软件开发中的重要性

软件测试的某些方面经常会在那些刚接触流程的人中造成混淆——例如在稳定性和可靠性测试之间划清界限。 两者通常可以互换使用&#xff0c;并且有一个共同的目标&#xff0c;即确保系统可以在选定的时间范围内稳定运行。 在这篇文章中&#xff0c;我们将仔细研究什么是稳定性测…

10年Java开发,准备去腾讯了~

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;又得准备面试了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约时间&a…

chatgpt赋能python:Python分词,助力文本处理和搜索引擎优化

Python分词&#xff0c;助力文本处理和搜索引擎优化 作为一种广泛应用于文本处理的编程语言&#xff0c;Python在分词处理方面也有着得天独厚的优势。Python分词不仅可以帮助我们完成文本处理任务&#xff0c;还能够为搜索引擎优化提供便利。 什么是分词&#xff1f; 分词&a…

集线器与交换机的区别

1.集线器与交换机的区别 笔记来源&#xff1a; 湖科大教书匠&#xff1a;集线器与交换机的区别 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 1.1 集线器 早期总线型以太网的机械连接点不太可靠 使用双绞线和集线器相对可靠 集线器可以在物理层…

计算机网络和因特网概述

一.计算机网络和因特网 计算机网络是指多台计算机通过通信线路或其他连接方式相互连接起来&#xff0c;实现信息交换和共享的系统。而因特网是利用标准化协议互联起来的全球性计算机网络&#xff0c;是计算机网络的一种特殊形式。 1.1 什么是因特网 1.1.1 从具体构成描述 因…

day04——特征处理之特征降维

特征处理之特征降维 一、特征选择Filter(过滤式)1&#xff0c;低方差特征过滤2、相关系数 Embedded (嵌入式) 二、主成分分析&#xff08;PCA&#xff09; 特征降维&#xff1a;如果特征本身存在问题或者特征之间相关性较强&#xff0c;对于算法学习预测会影响较大。降维是指在…

设计模式-解释器模式

解释器模式 问题背景解释器模式基本介绍原理类图 使用解释器模式来解决问题UML类图代码示例运行结果 注意事项和细节 问题背景 通过解释器模式来实现一个表达式的加减运算 例如&#xff1a;在我们先输入一个表达式&#xff1a;abc-de&#xff0c;然后输入a&#xff0c;b&#…

layui框架实战案例(22):layui-tab-title的宽度自适应的解决方案

HTML源代码 <div class"layui-card"><div class"layui-card-header">卡片风格</div><div class"layui-card-body"><div class"layui-tab layui-tab-card"><ul class"layui-tab-title">…

酷游SVG-地址路径

说到SVG中最复杂的东西&#xff0c;路径(Path)绝对是一个大魔王&#xff0c;它有太多的commands可以用来定义路径。【娜娜提供酷游地kw9㍠neт地址】透过Path虽然可以绘制效果不错的SVG图形&#xff0c;但是要自己定义一个个坐标点&#xff0c;再去把它们完美的串连在一起&…

《银行从业法律法规》第二章——金融基础知识

第二章 金融基础知识 第二节 货币政策 【 知识点1】 货币政策目标 制定和实施货币政策&#xff0c; 首先必须明确货币政策最终要达到的目的&#xff0c; 即货币政策的最终目标。中央银行通过货币政策工具操作直接引起操作目标的变动&#xff0c;操作目标的变动又通过一定的途…

libarchive 如何在Ubuntu中进行安装

简介 libarchive是一个开源的压缩和归档库。 它支持实时访问多种压缩文件格式&#xff0c;比如7z、zip、cpio、pax、rar、cab、uuencode等&#xff0c;因此应用十分广泛。 举个例子&#xff0c;我写了一段代码&#xff0c;想要解压一个压缩包&#xff0c;那我就得 fork 一个 …

python制作简单版天天酷跑,是不是你平日里摸鱼小游戏呀

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 天天酷跑是一款轻松好玩、简单时尚的跑酷类手机游戏。 那我们能不能用python模拟出这个一个游戏呢&#xff1f; 答案当然是可以的&#xff0c;今天我就给大家带来简易版的天天酷跑小游戏 开发环境&#xff1a; 版 本&a…

如何用Python写个网页爬取程序

如何用Python写个网页爬取程序 准备开发工具安装PythonPython安装pipPip安装爬取插件准备好网页地址代码实现 准备开发工具 额&#xff0c;作者用的是vscode。具体怎么安装自行百度哈&#xff0c;这个都不会建议就不要学爬取了。 不忍心藏着也&#xff0c;给你个方法吧 vsc…

2023年第六届广西大学生程序设计竞赛(热身赛)题解

题目均来自去年的省赛原题 参考资料 知乎&#xff1a;第五届GXCPC广西大学生程序设计竞赛 部分题解&#xff08;无CDK&#xff09; A题送分题&#xff0c;跳过 B 位运算lowbit函数 题目大意&#xff1a; 对一个数&#xff08;二进制&#xff09;进行操作&#xff0c;询问使其…

C++11/C++14中constexpr的使用

常量表达式(const expression)是指值不会改变并且在编译过程中就能得到计算结果的表达式。字面值属于常量表达式&#xff0c;用常量表达式初始化的const对象也是常量表达式。 只要有可能使用constexpr&#xff0c;就使用它。 C11中constexpr的使用&#xff1a; constexpr是C11中…

MKS SERVO4257D 闭环步进电机_系列7 MODBUS-RTU通讯示例

第1部分 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口和RS485/CAN串行接口&#xff0c;支持MODBUS-RTU通讯协议&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&a…

论坛项目学习记录【预备篇2】

论坛项目学习记录【预备篇2】 1. 什么是依赖注入2. 怎么使用依赖注入依赖注入注意事项 3.组件扫描情况下依赖注入的实现4.Resource注解的使用与Autowired的区别5.SpringMvcSpringMvc执行流程 1. 什么是依赖注入 就是在Spring容器内容将各个对象的依赖关系建立好的操作&#xf…

持续集成和持续交付:构建高效的软件交付流水线

在现代软件开发中&#xff0c;持续集成&#xff08;Continuous Integration&#xff09;和持续交付&#xff08;Continuous Delivery&#xff09;已成为构建高效、可靠软件交付流水线的关键实践。通过自动化和频繁地集成代码、构建、测试和部署&#xff0c;团队能够更快地交付高…

Unity--使用Cinemachine Confiner设置摄像机边界

使用Cinemachine Confiner设置摄像机边界 前提提要&#xff1a;在做这个功能前需要&#xff1a; ​ main camera ​ 另外一个相机 思路&#xff1a;创建一个对象绑定Polygon Collider2D 边界。然后在另外一个相机Cinemachine Confiner上绑定他 ​ 绑定边界 记得点这个&#…

2023年京东618全品类预售数据查询

这一期主要分享今年618京东数码产品的预售数据&#xff0c;包括笔记本电脑、投影机、微单相机三大品类。 -笔记本电脑- 今年618&#xff0c;笔记本电脑在京东累计预售量达到73万件&#xff0c;预售额达到41亿元。预售期间&#xff0c;微单相机品类均价在5661元左右。期间&#…