拓扑排序-java

news2024/12/26 22:23:04

主要通过宽度优先搜索(BFS)来实现有向无环图的拓扑序列,邻接表存储图。数组模拟单链表、队列,实现BFS基本操作。

文章目录

前言

一、有向图的拓扑序列

二、算法思路 

 1.拓扑序列

2.算法思路

三、使用步骤

1.代码如下(示例):

2.读入数据:

3.代码运行结果

总结


前言

主要通过宽度优先搜索(BFS)来实现有向无环图的拓扑序列,邻接表存储图。数组模拟单链表、队列,实现BFS基本操作。


提示:以下是本篇文章正文内容,下面案例可供参考

一、有向图的拓扑序列

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。

若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。

输出格式

共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。

否则输出 −1。

数据范围

1≤n,m≤100000

二、算法思路 

 1.拓扑序列

图1.1拓扑序列示例 

有向图的拓扑排序是指将有向无环图(DAG)中的所有顶点排成一个线性序列,使得对于图中的每一条有向边 (u, v),顶点 u 在序列中都出现在顶点 v 的前面。换句话说,如果图中存在一条从顶点 u 到顶点 v 的有向边,那么在拓扑排序中顶点 u 将在顶点 v 的前面。

在有向图中,每个顶点都有两种相关的度量:入度(in-degree)和出度(out-degree)。这些度量用于描述有向图中顶点之间的关系。

  • 入度:一个顶点的入度是指指向该顶点的边的数量。换句话说,对于顶点v,它的入度是有多少条边以v为终点。

  • 出度:一个顶点的出度是指从该顶点发出的边的数量。换句话说,对于顶点v,它的出度是有多少条边以v为起点。

各结点出入度示例
结点入度出度
102
211
320

一个有向无环图一定至少存在一个入度为0的点。 

开始入度为0的结点都是初始结点,然后当我们打印该结点,就将它指向的结点的入度擦去,然后我们在找入度为0的结点当作下一个结点重复上述操作,我们就可以得到该有向无环图的拓扑序列。

本次用宽度优先搜索来实现有向图的拓扑序列!

注:有向无环图才有拓扑序列,无向图是没有的。

2.算法思路

我们采用宽度优先搜索来实现上述操作。我们还是用邻接表法来存储图。我们还是用一维整型数组head,其中head[i]表示结点i相连的边,对应的边用单链表存储;还是用数组来模拟单链表,一维整型数组e用来存储结点的值,一维整型数组存储该结点指向的下一结点在e数组中的索引,整型变量index表示新创建的结点在e数组中的索引。

 添加a指向b边我们只需要在head数组中对应结点 a 的单链表中插入结点 b 即可。单链表的插入具体细节请看这篇博客单链表-java-CSDN博客。

    public static void add(int a, int b){
        e[index] = b;
        ne[index] = head[a];
        head[a] = index++;
    }

数组模拟单链表是基础!!! 

我们引入一个一维整型数组d,来存储图中每个点的入度;一维整型数组queue来模拟队列,整型变量hh时队头指针,整型变量rear时队尾指针。当这个点的入度为0时,我们就把它当作拓扑序列的起点,放入队列;当队列不为空时,我们弹出一个结点即t = queue[hh++];我们来遍历与结点 t相连的点,找到与结点 t 相连的点 j ,将结点 j 对应的入度减1 即 d[j]--;如果该结点的入度为0的话,就说明该结点没有上一个结点指向它,我们就让它入队。当队列为空时,如果我们的队尾指针等于n -1 的话就说明我们已经找出对应的拓扑序列。如果图中存在环的话,那么我们我们的队列为空的时候,队尾指针根本不可能到 n-1,一定比这个值小。

拓扑序列的本质就是我们每次把入度为0的结点打印出来,然后该结点指向的所有的结点的入度减1,然后再打印入度为0的结点。

所以拓扑排序就是对应队列数组从0到n - 1打印即可。

三、使用步骤

1.代码如下(示例):


import java.io.*;
import java.util.Arrays;


public class Main {
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int n,m;
    static int N = 100100;
    static int[] head = new int[N];
    static int[] e = new int[N];
    static int[] ne = new int[N];
    static int index;
    //存储每个点的入度
    static int[] d = new int[N];
    static int[] queue = new int[N];
    public static void main(String[] args) throws Exception{
        n = nextInt();
        m = nextInt();
        Arrays.fill(head,-1);
        while (m-- > 0){
            int a = nextInt();
            int b = nextInt();
            add(a,b);
            d[b]++;
        }
        if(topSort()){
            for(int i = 0;i < n;i++){
                pw.print(queue[i]+" ");
            }
        }else {
            pw.println("-1");
        }
        pw.flush();
    }
    public static boolean topSort(){
        int hh = 0;
        int rear = -1;
        //将起点放入队列
        for(int i = 1;i <= n;i++){
            if(d[i] == 0){
                queue[++rear] = i;
            }
        }
        while (hh <= rear){
            int t = queue[hh++];
            for(int i = head[t]; i != -1;i = ne[i]){
                //t指向j
                int j = e[i];
                d[j]--;
                if(d[j] == 0){
                    queue[++rear] = j;
                }
            }
        }
        return rear == n - 1;
    }
    public static void add(int a, int b){
        e[index] = b;
        ne[index] = head[a];
        head[a] = index++;
    }
    public static int nextInt()throws Exception {
        st.nextToken();
        return (int)st.nval;
    }
}

2.读入数据:

3 3
1 2
2 3
1 3

3.代码运行结果

1 2 3 

总结

上述主要通过宽度优先搜索来实现有向无环图的拓扑序列,其中还是很常用的邻接表存储图。数组模拟单链表、队列,然后宽度优先搜索3部曲,大致就这些。

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

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

相关文章

【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【西瓜书】大题

1.线性回归 思路&#xff1a;ywxb&#xff0c;w为一维数组&#xff0c;求均方误差MSE&#xff0c;对w和b分别求偏导为0得到关于w和b的闭式求解。预测第十年的代入ywxb求解即可。 2.查准率、查全率 思路&#xff1a;先计算每个算法测试结果的混淆矩阵&#xff0c;再根据混淆矩阵…

Matlab|混合策略改进的蝴蝶优化算法

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序主要对蝴蝶算法&#xff08;BOA&#xff09;进行改进&#xff0c;参考文献《基于改进蝴蝶优化算法的冗余机器人逆运动学求解》&#xff0c;有如下改进策略&#xff1a; 改进1&#xff1a;采用反向学习策…

RK3568平台(显示篇)FrameBuffer 应用编程

一.FrameBuffer介绍 FrameBuffer&#xff08;帧缓冲器&#xff09;是一种计算机图形学概念&#xff0c;用于在显示器上显示图形和文本。在 计算机显示系统中&#xff0c;FrameBuffer 可以看作是显存的一个抽象概念&#xff0c;用于存储显示屏幕上显示 的像素点的颜色和位置信息…

ElementUi el-tree动态加载节点数据 load方法触发机制

需求背景&#xff1a;需要根据点击后获取的数据动态渲染一个 el-tree&#xff0c;同时渲染出来的 el-tree&#xff0c;需要点击节点时才能获取该节点的层数的获取&#xff0c;如图所示&#xff0c;我需要点击“组”节点才能渲染“设备列表”树&#xff0c;同时“设备列表”树的…

Vue16-绑定class样式

一、vue绑定class样式 1-1、需求一&#xff1a;字符串写法 vue实现class样式绑定 1-2、需求二 点击div&#xff0c;随机切换样式。 math.random()&#xff1a;随机数的范围[0, 1) 1-3、需求三&#xff1a;数组写法 样式的追加 1-4、需求四 &#xff1a;对象写法 二、vue绑定…

浅解Reids持久化

Reids持久化 RDB redis的存储方式&#xff1a; rdb文件都是二进制&#xff0c;很小&#xff0c;里面存的是数据 实现方式 redis-cli链接到redis服务端 使用save命令 注&#xff1a;不推荐 因为save命令是直接写到磁盘里面&#xff0c;速度特别慢&#xff0c;一般都是redis…

MySQl基础----Linux下搭建mysql软件及登录和基本使用(附实操图超简单一看就会)

绪论​ 涓滴之水可磨损大石&#xff0c;不是由于他力量强大&#xff0c;而是由于昼夜不舍地滴坠。 只有勤奋不懈地努力&#xff0c;才能够获得那些技巧。 ——贝多芬。新开MySQL篇章&#xff0c;本章非常基础包括如何在Linux上搭建&#xff08;当然上面的SQL语句你在其他能执行…

UnityXR Interaction Toolkit 如何使用XRHand手部识别

前言 Unity的XR Interaction Toolkit是一个强大的框架,允许开发者快速构建沉浸式的VR和AR体验。随着虚拟现实技术的发展,手部追踪成为了提升用户交互体验的关键技术之一。 本文将介绍如何在Unity中使用XR Interaction Toolkit实现手部识别功能。 准备工作 在开始之前,请…

Chat-TTS:windows本地部署实践【有手就行】

最近Chat-TTS模型很火&#xff0c;生成的语音以假乱真&#xff0c;几乎听不出AI的味道。我自己在本地部署玩了一下&#xff0c;记录一下其中遇到的问题。 环境&#xff1a; 系统&#xff1a;windows 11 GPU&#xff1a; Nvidia 4060 Cuda&#xff1a;12.1&#xff08;建议安…

后方碰撞预警系统技术规范(简化版)

后方碰撞预警系统技术规范(简化版) 1 系统概述2 预警区域3 预警目标4 功能需求功能条件5 显示需求6 指标需求1 系统概述 后方碰撞预警系统RCW(Rear Collision Warning)是在后方车辆即将与自车发生碰撞之前,激活危险警告灯以较高频率闪烁,从而吸引后方驾驶员的注意力,避免…

PG 数据库常用参数调整

1.shard_buffers Postgresql使用自己的缓冲区,也使用操作系统缓冲区。这意味着数据存储在内存中两次,首先是 Postgresql缓冲区,然后是操作系统缓冲区。 与其他数据库不同, Postgresql不提供直接IO。这称为双缓冲&#xff08;就是磁盘中的时候读的时候先放在数据库的缓冲区&am…

【Python教程】3-控制流、循环结构与简单字符串操作

在整理自己的笔记的时候发现了当年学习python时候整理的笔记&#xff0c;稍微整理一下&#xff0c;分享出来&#xff0c;方便记录和查看吧。个人觉得如果想简单了解一名语言或者技术&#xff0c;最简单的方式就是通过菜鸟教程去学习一下。今后会从python开始重新更新&#xff0…

Vue17-条件渲染

一、使用v-show属性做条件渲染 控制元素的显示和隐藏 v-show里面也能是表达式&#xff0c;只要表达式的值是boolean就行。 或者 当时结构还在&#xff1a; 二、使用v-if属性做条件渲染 结构也不在了 三、示例 方式一&#xff1a; 方式二&#xff1a; 当元素有很高的切换频率&am…

【web本地存储】storage事件,StorageEvent对象介绍

storage事件 Web Storage API 内建了一套事件通知机制&#xff0c;当存储区域的内容发生改变&#xff08;包括增加、修改、删除数据&#xff09;时&#xff0c;就会自动触发storage事件&#xff0c;并把它发送给所有感兴趣的监听者&#xff0c;因此&#xff0c;如果需要跟踪存…

第十二届蓝桥杯单片机国赛练习代码

文章目录 前言一、问题重述二、主函数总结 前言 第十五蓝桥杯国赛落幕已有十天&#xff0c;是时候总结一下&#xff0c;这个专栏也将结束。虽然并没有取得预期的结果&#xff0c;但故事结尾并不总是美满的。下面是赛前练习的第十二届国赛的代码。 一、问题重述 二、主函数 完整…

万向节锁死(Gimbal Lock)

Gimbal Lock是一个常见的3D动画问题,主要由旋转顺序引起的。我来详细解释一下它的成因: 在三维空间中,任何旋转都可以分解为绕X,Y,Z三个轴的欧拉旋转(Euler Rotation)。每个轴的旋转是按照一定顺序进行的,比如XYZ或ZYX等。 理论上,通过这三个旋转值的组合,可以达到任意的空间…

MATLAB实现磷虾算法(Krill herd algorithm)

1.算法介绍 磷虾算法&#xff08;Krill Herd Algorithm, KH&#xff09;是一种基于生物启发的优化算法&#xff0c;其原理模拟了南极磷虾&#xff08;Euphausia superba&#xff09;群体的聚集行为。该算法旨在通过模拟磷虾个体间的相互作用、觅食行为和随机扩散&#xff0c;来…

设计模式 —— 观察者模式

设计模式 —— 观察者模式 什么是观察者模式观察者模式定义观察者模式的角色观察者模式的使用场景观察者模式的实现 被观察者&#xff08;Subject&#xff09;观察者&#xff08;Observer&#xff09;通知&#xff08;notify&#xff09;更新显示&#xff08;update&#xff09…

Webpack 从入门到精通-基础篇

一、webpack 简介 1.1 webpack 是什么 webpack 是一种前端资源构建工具&#xff0c;一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。 它将根据模块的依赖关系进行静态分析&#xff0c;打包生成对应的…