拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)

news2024/9/27 12:10:49

前置知识

有向无环图

在图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。
如图所示。
在这里插入图片描述

入度

对于一个有向图,若x点指向y点,则称x点为y点的入度。

出度

对于一个有向图,若x点指向y点,则称y点为x点的出度。

队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

我们可以用双指针标记一下,通过front指针与rear指针,对队头和队尾进行标记,然后只允许在front、rear指针的位置进行增删改查,那么这样便实现了对数组的受限。这是一种运用数组的数据结构对队列的模拟。初学者建议先用这种方式熟悉队列。

具体操作:

/*
    通常将front赋值为0,rear赋值为-1
    方便后续进队、出队以及取队首元素
 */
int a[100], front=0, rear=-1;

// 进队
a[++rear] = 10;

// 出队
front++

// 取队首元素
a[front]

// 取队尾元素
a[rear]

// 判断是否为空队
if(front > rear)
    cout << "该队列为空队";

不过,到了后期,为了节省时间,我们可以直接用c++自带的STL容器来完成操作。

具体操作如下:

// 导入queue包
#include<queue>

// 申明一个queue对象
// 填入你想装填的数据类型
queue<int> qu;

// 进队
int a = 10;
qu.push(a);

// 出队,无返回值
qu.pop();

// 取队首元素
int front = qu.front();

概述

今天我们来学拓扑排序
什么是拓扑排序呢?
百度百科这样说:

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

什么意思呢?

俗话说的好:

实践是真理的试金石

那么,就让我们举一个例子吧!
如图,1、2、3、4、5几个点组成了一个图。


那么,1,2,4,3,6,5或1,2,4,3,5,6就是它的拓扑序

但是,这是如何实现的呢?请看下面的算法原理

算法原理

下面我将使用图文结合的方式演示拓扑排序的算法原理。

还是上面那张图。

首先,将入度为0的入队。上图中是点1。

在这里插入图片描述
然后,用宽搜遍历队列
对每个点的每轮遍历步骤如下:

  • 将这个点出队,并加入拓扑数组中
  • 遍历这个点的所有出度
  • 将其出度点的入度数量减少一
  • 如果其出度点入度为0,入队

那么,一轮下来,就变成这样子了:
在这里插入图片描述
以此类推。

遍历到点3时,是这样的:
在这里插入图片描述
……

然后,就没有然后了,一切结束。
在这里插入图片描述
如图所示,其拓扑序为1,2,4,3,5,6。当然,也可以是1,2,4,3,6,5。

算法实现

这可以变为以下的问题。我称为拓扑排序元问题

给你一个有向无环图,请输出它的拓扑序(m条有向边,n个结点)

建图(邻接表存图)

首先,我们要建图
这里采用邻接表建图。

邻接表是什么?
以点为一个结点,用其邻接点建表

怎么这么朦胧?好吧,偷懒了,自己查百度……

看到这里,就默认你会了建图操作。那么,放一下代码。
(此处设定为m条有向边)

for(int i=1;i<=m;i++)
{
	int x,y;
	scanf("%d%d",&x,&y);
	rd[y]++;
	e[x].push_back(y);
}

rd[y]++ 是什么意思?请往后看。

入队

上面提到,首先,将入度为0的点入队。
那么,我们就遍历一遍n个点,当遇到入度为0的点时,入队。

如何判断入度为0?

这时前面的 rd数组 就有用了。它是用于统计入度数的。
而前面为何是rd[y]++?
因为是 x指向y,因此y入度数加1

入队操作

非常简单!这里为了省力,用了STL容器
只需要**q.push(i)**一下就可以了

代码

	queue<int>q;
	for(int i=1;i<=n;i++)
	{
		if(rd[i]==0) q.push(i);
		//入度为0,入队 
	}

核心部分

这部分的过程,我在前面是这样说的:

用宽搜遍历队列。
那就宽搜。

宽搜

没遍历到一个点,就将其弹出,并压入拓扑数组。

对每个点的操作

我在前面这样说:
对每个点的每轮遍历步骤如下:
1.将这个点出队,并加入拓扑数组中
2.遍历这个点的所有出度
3.将其出度点的入度数量减少一
4.如果其出度点入度为0,入队

那就照着做嘛。
很简单,实在看不懂代码中有注释。

代码

	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		topu.push_back(x);//推入拓扑数组 
		for(auto y:e[x])
		{
			rd[y]--;//删掉一条边 
			if(rd[y]==0)//入度为0 
			{
				q.push(y);//入队 
			}
		}
	}

好,大功告成!

算法元代码

上面没看懂的话,看下面的代码,含有注释。

#include<bits/stdc++.h>
using namespace std;
const int NN=5005;
int n,m,rd[NN];
//rd[i]表示i点的入度数 
vector<int>e[NN],topu;
//e作为邻接表存储用,topu储存拓扑序 
void tuopu()
{
	queue<int>q;
	for(int i=1;i<=n;i++)
	{
		if(rd[i]==0) q.push(i);
		//入度为0,入队 
	}
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		topu.push_back(x);//推入拓扑数组 
		for(auto y:e[x])
		{
			rd[y]--;//删掉一条边 
			if(rd[y]==0)//入度为0 
			{
				q.push(y);//入队 
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		rd[y]++;//x指向y,因此y入度数加1 
		e[x].push_back(y);//加边 
	}
	tuopu();
	for(auto x:topu)
	{
		printf("%d ",x);//输出拓扑序 
	}
} 

总结

拓扑排序就是这回事:
首先,将入度为0的点入队。
然后,用宽搜遍历队列。
在此之后,对每个点进行如下操作:
1.将这个点出队,并加入拓扑数组中
2.遍历这个点的所有出度
3.将其出度点的入度数量减少一
4.如果其出度点入度为0,入队

下一篇文章,我将会详细讲解拓扑排序相关例题。
好,期待三连~~

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

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

相关文章

Redis --- 多级缓存

一、什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; 请求要经过Tomcat处理&#xff0c;Tomcat的性能成为整个系统的瓶颈Redis缓存失效时&#xff…

python每日一练:硬币划分(多方法详解)

文章目录 前言0、题目一、暴力总是不能解决问题的二、还能更暴力一点三、减少暴力思想四、引入先进思想总结 前言 这题挺有意思的&#xff0c;典型的背包组合问题&#xff0c;虽然没有要求各种组合方式&#xff0c;不过我们可以试试给出组合方式。当然这题不太可能用一行代码解…

STM32 从入门到精通系列讲解 - 总目录

&#x1f466; 作者介绍&#xff1a;Bazinga bingo&#xff0c;专注C语言应用硬核干货分享&#xff0c;潜心修炼&#xff0c;虚心学习&#xff0c;立志做嵌入式相关赛道的Top。 &#x1f4d5; 本文收录于《STM32开发》专栏&#xff0c;包含STM32内部模块介绍、片内资源开发、不…

如何使用bingChat(使用方法+遇到的问题+感受)

文章目录 前言一、如何使用Bing Chat1. 下载new Bing2.重新注册一个microsoft&#xff08;此步骤可略过&#xff0c;如有问题再操作此步骤&#xff09;3. 使用 Bing Chat 二、常见问题1.Chat mode is only available when you have access to the new Bing.2. 网页上没有“聊天…

leetcode 104——二叉树的最大深度

文章目录 题目详情方法一 万能的递归方法二 通过使用层序遍历的方式Java完整代码递归实现非递归实现——借助队列 题目详情 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 l…

yolov5图像识别voc转yolo代码解析

https://github.com/ultralytics/JSON2YOLO https://blog.csdn.net/qq_51831335/article/details/127237772 目标检测数据集标签转换COCO2VOC、YOLO2VOC、JSON2YOLO <annotation><folder>VOC2007</folder><filename>000001.jpg</filename><s…

x265码控分析

D和R的关系 高分辨率量化 均匀量化&#xff1a;量化区间 ‘ Δ k y k − y k − 1 ‘ \Delta_ky_k-y_{k-1} ‘Δk​yk​−yk−1​‘&#xff0c;近似为常数&#xff1b;p(x)为信源概率密度函数&#xff0c;且 ‘ Δ k ‘ \Delta_k ‘Δk​‘的大小相对于p(x)的变化率充分小&…

【模拟IC学习笔记】 反馈

反馈的作用&#xff1a;增益灵敏度降低 采用开环的方式实现一个精确的增益比较困难&#xff0c;但是可以实现高增益。 增益灵敏度衍生出来的另外两个特点 1、增加系统带宽。 2、改变输出阻抗&#xff0c;提高驱动能力。 反馈的作用&#xff1a;增加带宽 带宽的增加来源于…

对传递函数的零极点、频率响应、稳定性的理解

对传递函数的零极点、频率响应、稳定性的理解 零极点 从传递函数求零极点 令传递函数分子为0求出零点&#xff0c;令分母为0求出零点。 频率响应 单极点系统的频率响应 A v A v d c ∗ ( 1 / ( 1 s R C ) ) AvAv_dc*(1/(1sRC)) AvAvd​c∗(1/(1sRC))&#xff0c;系统的极…

python通过SSH管道访问ClickHouse

目录 前言什么是跳板机什么是SSH协议SSH管道访问ClickHouse参考文献 前言 因为新业务需要&#xff0c;数据都存储在阿里云服务器的ClickHouse数据库里&#xff0c;最近想取点数探索一下&#xff0c;于是下载了客户端工具DBeaver并成功连接ClickHouse&#xff0c;然后想通过pyt…

【前端面试题】这些js功能你一定要学会

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 1.图片失败&#xff0c;重新加载 如果图片资源不存在&#xff0c;那可以设置图片失败的占位…

深度学习——A3C算法

A3C算法&#xff08;Asynchronous Advantage Actor-Critic&#xff09; DDPG算法之后&#xff0c;DeepMind对其改造&#xff0c;提出了效果更好的 Asynchronous Advantage Actor-Critic&#xff08;A3C&#xff09;算法&#xff08;论文是 Asynchronous Methods for Deep Rein…

【谷粒商城之远程调用和异步调用丢失请求头问题】

本笔记内容为尚硅谷谷粒商城远程调用和异步调用丢失请求头问题部分 目录 一、Feign远程调用丢失请求头 二、Feign异步调用丢失请求头问题 一、Feign远程调用丢失请求头 ​ ​ 问题&#xff1a; feign在远程调用之前要构造请求&#xff0c;调用了很多的拦截器。 浏览器发送请…

u01使用率100%报错归档满的问题

今天下午客户报数据库无法连接了&#xff0c;我也立刻登录查看 因为显示orcl1归档满了&#xff0c;我就登录查看磁盘组的空间&#xff0c;发现空间空余很多 就sqlpus登录了&#xff0c;发现u01使用率满了 [oracledb1 ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 …

《面试1v1》动态代理

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 那你能说一下反射和动态代理的关系吗&#xff1f; 候选人&#xff1a; 当然可以。动态代理是一种基于反射的机制&#xff0c;它可以在运行时动…

动设备维护管理的新趋势——在线监测与故障诊断系统

随着工业自动化水平的提高&#xff0c;动设备在现代工业中扮演着越来越重要的角色。然而&#xff0c;动设备故障率高、维修难度大、维护费用高是工厂面临的重要挑战之一。针对这些问题&#xff0c;在线监测与故障诊断系统逐渐成为动设备维护管理的新趋势。 图.设备工程师正在维…

Spring Cloud Alibaba: Gateway 网关过滤器 GatewayGatewayFilter factory (记录)

目录 AddRequestHeader GatewayFilter factory AddRequestHeadersIfNotPresent GatewayFilter factory AddRequestParameter GatewayFilter Factory AddResponseHeader GatewayFilter Factory CircuitBreaker GatewayFilter factory circuit breaker based on the status…

真题详解(构造二叉树)-软件设计(六十八)

真题详解&#xff08;归纳法&#xff09;-软件设计&#xff08;六十七)https://blog.csdn.net/ke1ying/article/details/130517187 CMM能力成熟模型 CL0(未完成)&#xff1a;过程域未执行或未得到定义的目标。 CL1(已执行)&#xff1a;将可标识的输入工作产品转换成可标识的…

数组中的empty剖析

数组中的empty剖析 一、首先empty是怎么来的 直接通过new Array来新建&#xff0c;手动修改数组的length&#xff0c;逗号之间没有任何数据等 const array new Array(3); console.log(array); //* (3) [empty 3]const array2 [1, , 3]; console.log(array2); //* [1, e…

【软考|软件设计师】进程p1,p2,p3,p4,p5和p6的前趋图

目录 题目&#xff1a; 答&#xff1a; 题目&#xff1a; 进程p1,p2,p3,p4,p5和p6的前趋图如下图所示。用PV操作控制这6个进程之间同步与互斥 的程序如下&#xff0c;程序中的空(1)和空(2)处应分别为________,空(3)和空(4)处分别为________, 空(5)和空(6)处应分别为_______…