详解Java实现拓扑排序算法

目录
  • 一、介绍
  • 二、拓扑排序算法分析
  • 三、拓扑排序代码实现

一、介绍

百科上这么定义的:

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

为什么会有拓扑排序?拓扑排序有何作用?

举个例子,学习java系列的教程

代号 科目 学前需掌握
A1 javaSE
A2 html
A3 Jsp A1,A2
A4 servlet A1
A5 ssm A3,A4
A6 springboot A5

就比如学习java系类(部分)从java基础,到jsp/servlet,到ssm,到springboot,springcloud等是个循序渐进且有依赖的过程。在jsp学习要首先掌握java基础html基础。学习框架要掌握jsp/servlet和jdbc之类才行。那么,这个学习过程即构成一个拓扑序列。当然这个序列也不唯一,你可以对不关联的学科随意选择顺序(比如html和java可以随便先开始哪一个。)

那上述序列可以简单表示为:

其中五种均为可以选择的学习方案,对课程安排可以有参考作用,当然,五个都是拓扑序列。只是选择的策略不同!

一些其他注意:

DGA:有向无环图
AOV网:数据在顶点 可以理解为面向对象
AOE网:数据在边上,可以理解为面向过程!

而我们通俗一点的说法,就是按照某种规则将这个图的顶点取出来,这些顶点能够表示什么或者有什么联系。

规则:

  • 图中每个顶点只出现一次
  • A在B前面,则不存在B在A前面的路径。(不能成环!!!!)
  • 顶点的顺序是保证所有指向它的下个节点在被指节点前面!(例如A—>B—>C那么A一定在B前面,B一定在C前面)。所以,这个核心规则下只要满足即可,所以拓扑排序序列不一定唯一!

二、拓扑排序算法分析

正常步骤为(方法不一定唯一):

  • 从DGA图中找到一个没有前驱的顶点输出。(可以遍历,也可以用优先队列维护)
  • 删除以这个点为起点的边。(它的指向的边删除,为了找到下个没有前驱的顶点)
  • 重复上述,直到最后一个顶点被输出。如果还有顶点未被输出,则说明有环!

对于上图的简单序列,可以简单描述步骤为:

1:删除1或2输出

2:删除2或3以及对应边

3:删除3或者4以及对应边

4:重复以上规则步骤

这样就完成一次拓扑排序,得到一个拓扑序列,但是这个序列并不唯一!从过程中也看到有很多选择方案,具体得到结果看你算法的设计了。但只要满足即是拓扑排序序列。

另外观察 1 2 4 3 6 5 7 9这个序列满足我们所说的有关系的节点指向的在前面,被指向的在后面。如果完全没关系那不一定前后(例如1,2)

三、拓扑排序代码实现

对于拓扑排序,如何用代码实现呢?对于拓扑排序,虽然在上面详细介绍了思路和流程,也很通俗易懂。但是实际上代码的实现还是很需要斟酌的,如何在空间和时间上能够得到较好的平衡且取得较好的效率?

首先要考虑存储。对于节点,首先他有联通点这么多属性。遇到稀疏矩阵还是用邻接表比较好。因为一个节点的指向节点较少,用邻接矩阵较浪费资源

另外,如果是1,2,3,4,5,6这样的序列求拓扑排序,我们可以考虑用数组,但是如果遇到1,2,88,9999类似数据,可以考虑用map中转一下。

我们具体的代码思想为:

  • 新建node类,包含节点数值和它的指向(这里直接用list集合替代链表了)
  • 一个数组包含node(这里默认编号较集中)。初始化,添加每个节点指向的时候同时被指的节点入度+1!(A—>C)那么C的入度+1;
  • 扫描一遍所有node。将所有入度为0的点加入一个栈(队列)
  • 当栈(队列)不空的时候,抛出其中任意一个node(栈就是尾,队就是头,顺序无所谓,上面分析了只要同时入度为零可以随便选择顺序)。将node输出,并且node指向的所有元素入度减一。如果某个点的入度被减为0,那么就将它加入栈(队列)。
  • 重复上述操作,直到栈为空。

这里主要是利用栈或者队列储存入度只为0的节点,只需要初次扫描表将入度为0的放入栈(队列)中。

  • 这里你或许会问为什么。
  • 因为节点之间是有相关性的,一个节点若想入度为零,那么它的父节点(指向节点)肯定在它为0前入度为0,拆除关联箭头。从父节点角度,它的这次拆除联系,可能导致被指向的入读为0,也可能不为0(还有其他节点指向儿子)

至于具体demo:

package 图论;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

public class tuopu {
	static class node
	{
		int value;
		List<Integer> next;
		public node(int value) {
			this.value=value;
			next=new ArrayList<Integer>();
		}
		public void setnext(List<Integer>list) {
			this.next=list;
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		node []nodes=new node[9];//储存节点
		int a[]=new int[9];//储存入度
		List<Integer>list[]=new ArrayList[10];//临时空间,为了存储指向的集合
		for(int i=1;i<9;i++)
		{
			nodes[i]=new node(i);
			list[i]=new ArrayList<Integer>();
		}
		initmap(nodes,list,a);

		//主要流程
		//Queue<node>q1=new ArrayDeque<node>();
		Stack<node>s1=new Stack<node>();
		for(int i=1;i<9;i++)
		{
			//System.out.print(nodes[i].next.size()+" 55 ");
			//System.out.println(a[i]);
			if(a[i]==0) {s1.add(nodes[i]);}

		}
		while(!s1.isEmpty())
		{
			node n1=s1.pop();//抛出输出

			System.out.print(n1.value+" ");

			List<Integer>next=n1.next;
			for(int i=0;i<next.size();i++)
			{
				a[next.get(i)]--;//入度减一
				if(a[next.get(i)]==0)//如果入度为0
				{
					s1.add(nodes[next.get(i)]);
				}
			}
		}
	}

	private static void initmap(node[] nodes, List<Integer>[] list, int[] a) {
		list[1].add(3);
		nodes[1].setnext(list[1]);
		a[3]++;
		list[2].add(4);list[2].add(6);
		nodes[2].setnext(list[2]);
		a[4]++;a[6]++;
		list[3].add(5);
		nodes[3].setnext(list[3]);
		a[5]++;
		list[4].add(5);list[4].add(6);
		nodes[4].setnext(list[4]);
		a[5]++;a[6]++;
		list[5].add(7);
		nodes[5].setnext(list[5]);
		a[7]++;
		list[6].add(8);
		nodes[6].setnext(list[6]);
		a[8]++;
		list[7].add(8);
		nodes[7].setnext(list[7]);
		a[8]++;

	}
}

输出结果

2 4 6 1 3 5 7 8

当然,上面说过用栈和队列都可以!如果使用队列就会得到1 2 3 4 5 6 7 8 的拓扑序列

以上就是详解Java实现拓扑排序算法的详细内容,更多关于Java实现拓扑排序算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解C++实现拓扑排序算法

    一.拓扑排序的介绍 拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行.为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行.通常,我们把这种顶点表示活动.边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简

  • python实现拓扑排序的基本教程

    拓扑排序 几乎在所有的项目,甚至日常生活,待完成的不同任务之间通常都会存在着某些依赖关系,这些依赖关系会为它们的执行顺序行程表部分约束.对于这种依赖关系,很容易将其表示成一个有向无环图(Directed Acyclic Graph,DAG,无环是一个重要条件),并将寻找其中依赖顺序的过程称为拓扑排序(topological sorting). 拓扑排序要满足如下两个条件 每个顶点出现且只出现一次. 若A在序列中排在B的前面,则在图中不存在从B到A的路径. 拓扑排序算法 任何无回路的顶点活动网(A

  • 详解图的应用(最小生成树、拓扑排序、关键路径、最短路径)

    1.最小生成树:无向连通图的所有生成树中有一棵边的权值总和最小的生成树 1.1 问题背景: 假设要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路.这时,自然会考虑这样一个问题,如何在最节省经费的前提下建立这个通信网.在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价.n个城市之间,最多可能设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少呢? 1.2 分析问题(建立模型): 可以用连通网来表示n个城市以及n个城市间可能设置的通信线

  • C++实现拓扑排序(AOV网络)

    本文实例为大家分享了C++实现拓扑排序的具体代码,供大家参考,具体内容如下 一.思路 先扫描所有顶点,把入度为0的顶点(如C,E)进栈.然后,取栈顶元素,退栈,输出取得的栈顶元素v(即入度为0的顶点v).接着,把顶点v的邻接顶点w的入度减1,如果w的入度变为0,则进栈.接着,取顶点w的兄弟结点(即取顶点v的邻接顶点w的下一邻接顶点),做同样的操作.重复上面步骤,直到输出n个顶点. 如上图: (1)扫描所有顶点,把入度为0的顶点进栈:将顶点C,E进栈: (2)取栈顶元素,退栈,输出取得的栈顶元素E

  • Python关于拓扑排序知识点讲解

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前. 通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列.简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序. 在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topolog

  • Java 排序算法整合(冒泡,快速,希尔,拓扑,归并)

    冒泡排序介绍 冒泡排序(Bubble Sort),又被称为气泡排序或泡沫排序. 它是一种较简单的排序算法.它会遍历若干次要排序的数列,每次遍历时,它都会从前往后依次的比较相邻两个数的大小:如果前者比后者大,则交换它们的位置.这样,一次遍历之后,最大的元素就在数列的末尾! 采用相同的方法再次遍历时,第二大的元素就被排列在最大元素之前.重复此操作,直到整个数列都有序为止! 冒泡排序图文说明 /* * a -- 待排序的数组 * n -- 数组的长度 */ public static void bub

  • Golang实现拓扑排序(DFS算法版)

    问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来.要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现. 该问题是一个非常典型的拓扑排序的问题,一般解决拓扑排序的方案是采用DFS-深度优先算法,对于DFS算法我的浅薄理解就是递归,因拓扑排序问题本身会有一些前置条件(本文不过多介绍拓扑算法的定义),所以解决该问题就有了以下思路. 先将排序要求声明成map(把map的key,value看作对顺序的要求,key应在value前出现),然后遍历1-5这几个数,将每次遍

  • 详解Java实现拓扑排序算法

    目录 一.介绍 二.拓扑排序算法分析 三.拓扑排序代码实现 一.介绍 百科上这么定义的: 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前.通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列.简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序. 为什么会有拓扑排序?拓

  • 详解Java双轴快速排序算法

    目录 一.前言 二.回顾单轴快排 三.双轴快排分析 3.1.总体情况分析 3.2.k交换过程 3.3.收尾工作 四.双轴快排代码 一.前言 首选,双轴快排也是一种快排的优化方案,在JDK的Arrays.sort()中被主要使用.所以,掌握快排已经不能够满足我们的需求,我们还要学会双轴快排的原理和实现才行. 二.回顾单轴快排 单轴快排也就是我们常说的普通快速排序,对于快速排序我想大家应该都很熟悉:基于递归和分治的,时间复杂度最坏而O(n2),最好和平均情况为O(nlogn). 而快排的具体思路也很

  • 详解Java如何实现FP-Growth算法

    FP-Growth算法的Java实现 这篇文章重点讲一下实现.需要两次扫描来构建FP树 第一次扫描 第一次扫描,过滤掉所有不满足最小支持度的项:对于满足最小支持度的项,按照全局支持度降序排序. 按照这个需求,可能的难点为如何按照全局支持度对每个事务中的item排序. 我的实现思路 扫描原数据集将其保存在二维列表sourceData中 维护一个Table,使其保存每个item的全局支持度TotalSup 在Table中过滤掉低于阈值minSup的项 将Table转换为List,并使其按照Total

  • Java实现拓扑排序算法的示例代码

    目录 拓扑排序原理 1.点睛 2.拓扑排序 3.算法步骤 4.图解 拓扑排序算法实现 1.拓扑图 2.实现代码 3.测试 拓扑排序原理 1.点睛 一个无环的有向图被称为有向无环图.有向无环图是描述一个工程.计划.生产.系统等流程的有效工具.一个大工程可分为若干子工程(活动),活动之间通常有一定的约束,例如先做什么活动,有什么活动完成后才可以开始下一个活动. 用节点表示活动,用弧表示活动之间的优先关系的有向图,被称为 AOV 网. 在 AOV 网中,若从节点 i 到节点 j 存在一条有向路径,则称

  • 详解Bucket Sort桶排序算法及C++代码实现示例

    桶排序(Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里.每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序).桶排序是鸽巢排序的一种归纳结果.当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n)).但桶排序并不是比较排序,他不受到O(n log n)下限的影响. 桶排序以下列程序进行: 1.设置一个定量的数组当作空桶子. 2.寻访序列,并且把项目一个一个放到对应的桶子去. 3.对每个不是空的桶子进行排

  • 详解Java Fibonacci Search斐波那契搜索算法代码实现

    一, 斐波那契搜索算法简述 斐波那契搜索(Fibonacci search) ,又称斐波那契查找,是区间中单峰函数的搜索技术. 斐波那契搜索采用分而治之的方法,其中我们按照斐波那契数列对元素进行不均等分割.此搜索需要对数组进行排序. 与二进制搜索不同,在二进制搜索中,我们将元素分成相等的两半以减小数组范围-在斐波那契搜索中,我们尝试使用加法或减法来获得较小的范围. 斐波那契数列的公式是: Fibo(N)=Fibo(N-1)+Fibo(N-2) 此系列的前两个数字是Fibo(0) = 0和Fibo

  • 详解Java实现分治算法

    目录 一.前言 二.分治算法介绍 三.分治算法经典问题 3.1.二分搜索 3.2.快速排序 3.3.归并排序(逆序数) 3.4.最大子序列和 3.5.最近点对 四.结语 一.前言 在学习分治算法之前,问你一个问题,相信大家小时候都有存钱罐的经历,父母亲人如果给钱都会往自己的宝藏中存钱,我们每隔一段时间都会清点清点钱.但是一堆钱让你处理起来你可能觉得很复杂,因为数据相对于大脑有点庞大了,并且很容易算错,你可能会将它先分成几个小份算,然后再叠加起来计算总和就获得这堆钱的总数了 当然如果你觉得各个部分

  • 详解Java中Dijkstra(迪杰斯特拉)算法的图解与实现

    目录 简介 工作过程 总体思路 实现 小根堆 Dijsktra 测试 简介 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等.注意该算法要求图中不存在负权边.对应问题:在无向图G=(V,E)中,假设每条边E(i)的长度W(i),求由顶点V0到各节点的最短路径. 工作过

  • 基数排序算法的原理与实现详解(Java/Go/Python/JS/C)

    目录 说明 实现过程 示意图 性能分析 代码 Java Python Go JS TS C C++ 链接 说明 基数排序(RadixSort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较.由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数.基数排序的发明可以追溯到1887年赫尔曼·何乐礼在列表机(Tabulation Machine)上的 基数排序的方式可以采用LSD(Least significant di

随机推荐