拓扑排序的思想?用代码怎么实现

news2024/11/28 12:56:16

目录

一、拓扑排序的思想

二、代码实现(C++)

代码思想

核心代码

完整代码


一、拓扑排序的思想

        以西红柿炒鸡蛋这道菜为例,其中的做饭流程为:

        中间2 6 3 7 4的顺序都可以任意调换,但1和5必须在最前面,这是饭前准备,8必须在最后面。1和5的入度为0,出度为1,8的入度都2,出度为0 

        在这个操作流程内,把每个步骤当作一个顶点,排序连接起来就是个有向图。

        排序,都是将元素按照一定的顺序/规则排列

        拓扑排序就是将元素按照先后顺序进行排序

        书面解释是:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为 AOV网(Activity On VertexNetwork)。AOV 网中的弧表示活动之间存在的某种制约关系。

        特点是:有向无环(无回路)

        拓扑序列:设G=(V,E)是一个具有 n个顶点的有向图V中的顶点序列 V1,V2····满足若从顶点到有一条路径,则在顶点序列中顶点必在顶点之前。则我们称这样的顶点序列为一个拓扑序列。

        拓扑排序就是对一个有向图构造拓扑序列的过程。构造时会有两个结果,如果此网的全部顶点都被输出,则说明它是不存在环(回路)的AOV 网,如果输出顶点数少了,哪怕是少了一个,也说明这个网存在环(回路),不是AOV 网

       拓扑排序的价值在于,不存在回路的AOV 网,我们可以将它应用在各种各样的工程或项目的流程图中,满足各种应用场景的需要。

二、代码实现(C++)

代码思想

        从入度为0的顶点开始访问,访问完成后,将以此顶点为狐尾的弧删除(此顶点的邻接表中的顶点入度都减少1),然后继续查找剩余顶点中入度为0的顶点,重复操作,直到所有顶点都被访问完,或者没有了入度为0的顶点(说明此AOV有回路)

        标记V1是从哪个顶点来的,借助栈来存储入度为0的顶点,栈的思想是先入后出

        需要借助邻接表来存储图,邻接表内添加一个属性:顶点的入度,当创建图时,路径中每添加一条边时,就将入度+1,如果是v0->b1,则v1的度+1

        先遍历度为0的节点,将其入栈,对栈顶元素在邻接表内查找其下一个顶点,并将下个顶点的度-1,设置为0。直到以该顶点所在的邻接表内的顶点的度都设置为0时,即代表将该顶点的狐尾删除。

        以该图为例,一张电影制作图,实现其中节点之间关联的排序

 

核心代码

int Graph::GetVertexIndex(char v)//获取顶点所在的下标
{
	int i;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_value == v)
			return i;
	}
	return -1;
}
void Graph::InsertVertex(char v)//插入顶点
{
	if (m_NumVertex >= m_MaxVertex)
		return;
	m_VerArr[m_NumVertex++].m_value = v;
}

void Graph::InsertEdge(char v1, char v2) //插入边
{
	int p1index = GetVertexIndex(v1);
	int p2index = GetVertexIndex(v2);
	if (p1index == -1 || p2index == -1)
		return;
	//v1-v2
	Edge* p = new Edge(p2index);
	p->m_next = m_VerArr[p1index].m_list;
	m_VerArr[p1index].m_list = p;
	m_VerArr[p2index].m_verIn++;
}
void Graph::TopologicalSort()//拓扑排序
{
	stack<int> ss;
	int i;
	Edge* p = NULL;
	for (i = 0; i < m_NumVertex; i++) //将入度为0的顶点入栈
	{
		if (m_VerArr[i].m_verIn == 0)
			ss.push(i);
	}
	for (i = 0; i < m_NumVertex; i++)//循环访问所有顶点进行拓扑排序
//该循环结束的条件:1.循环完,没有度为0的顶点再入栈即栈为空时,退出循环
	{
		if (ss.empty())
		{
			cout << "有回路" << endl;
			return;
		}
		else
		{
			int top = ss.top();//获取栈顶元素并出栈
			ss.pop();
			cout << m_VerArr[top].m_value << " ";//输出
			p = m_VerArr[top].m_list;//让P指向刚出来顶点的邻接表
			while (p)//循环遍历邻接表,设置入度-1
			{
				int in = --m_VerArr[p->m_destindex].m_verIn;//in为p所指向顶点的入度-1
				if (in == 0)
				{
					ss.push(p->m_destindex);//入度为0时,说明顶点就是入读为0的顶点,对其下标入栈
				}
				p = p->m_next;
			}
		}
	}
	cout << endl;
}

完整代码

#include<iostream>
using namespace std;
#define SIZE 20
class Edge //边
{
public:
	Edge() :m_next(NULL) {}
	Edge(int index) :m_destindex(index), m_next(NULL) {}
	int m_destindex;
	Edge* m_next;
};
class Vertex //顶点
{
public:
	Vertex() :m_list(NULL),m_verIn(0) {}
	Vertex(char v) :m_value(v), m_list(NULL),m_verIn(0) {}
	char m_value;
	Edge* m_list;
	int m_verIn;
};
class Graph
{
public:
	Graph();
	~Graph();
	void InsertVertex(char v);
	void InsertEdge(char v1, char v2);
	int GetVertexIndex(char v);
	void ShowGraph();
	void TopologicalSort();
private:
	int m_MaxVertex;
	int m_NumVertex;
	//Vertex* m_VerArr;
	Vertex m_VerArr[SIZE];
};

#include<stack>
void Graph::TopologicalSort()//拓扑排序
{
	stack<int> ss;
	int i;
	Edge* p = NULL;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_verIn == 0)
			ss.push(i);
	}
	for (i = 0; i < m_NumVertex; i++)
	{
		if (ss.empty())
		{
			cout << "有回路" << endl;
			return;
		}
		else
		{
			int top = ss.top();
			ss.pop();
			cout << m_VerArr[top].m_value << " ";
			p = m_VerArr[top].m_list;
			while (p)
			{
				int in = --m_VerArr[p->m_destindex].m_verIn;
				if (in == 0)
				{
					ss.push(p->m_destindex);
				}
				p = p->m_next;
			}
		}
	}
	cout << endl;
}
void Graph::ShowGraph()
{
	Edge* p = NULL;
	for (int i = 0; i < m_NumVertex; i++)
	{
		cout << i << " " <<m_VerArr[i].m_verIn<<" "<< m_VerArr[i].m_value << "->";
		p = m_VerArr[i].m_list;
		while (p != NULL)
		{
			cout << p->m_destindex << "->";
			p = p->m_next;
		}
		cout << "nul" << endl;
	}
}
int Graph::GetVertexIndex(char v)
{
	int i;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_value == v)
			return i;
	}
	return -1;
}
void Graph::InsertVertex(char v)
{
	if (m_NumVertex >= m_MaxVertex)
		return;
	m_VerArr[m_NumVertex++].m_value = v;
}
Graph::Graph()
{
	m_MaxVertex = SIZE;
	m_NumVertex = 0;
	//m_VerArr = new Vertex[m_MaxVertex];
}
Graph::~Graph()
{
	/*	if (m_VerArr != NULL)
		{
			delete[]m_VerArr;
			m_VerArr = NULL;
		}*/
	m_NumVertex = 0;
}
void Graph::InsertEdge(char v1, char v2)
{
	int p1index = GetVertexIndex(v1);
	int p2index = GetVertexIndex(v2);
	if (p1index == -1 || p2index == -1)
		return;
	//v1-v2
	Edge* p = new Edge(p2index);
	p->m_next = m_VerArr[p1index].m_list;
	m_VerArr[p1index].m_list = p;
	m_VerArr[p2index].m_verIn++;
}
void main()
{
	Graph g;//构建一个图
	g.InsertVertex('a');
	g.InsertVertex('b');
	g.InsertVertex('c');
	g.InsertVertex('d');
	g.InsertVertex('e');
	g.InsertVertex('f');
	g.InsertVertex('g');
	g.InsertVertex('h');
	g.InsertVertex('i');
	g.InsertVertex('j');
	g.InsertVertex('l');
	g.InsertVertex('m');
	g.InsertVertex('n');
	g.InsertVertex('o');
	g.InsertVertex('p');
	g.InsertVertex('q');
	g.InsertVertex('r');
	g.InsertEdge('a', 'b');
	g.InsertEdge('b', 'c');
	g.InsertEdge('b', 'd');
	g.InsertEdge('b', 'e');
	g.InsertEdge('c', 'f');
	g.InsertEdge('c', 'g');
	g.InsertEdge('d', 'g');
	g.InsertEdge('d', 'h');
	g.InsertEdge('e', 'h');
	g.InsertEdge('f', 'i');
	g.InsertEdge('g', 'i');
	g.InsertEdge('h', 'i');
	g.InsertEdge('i', 'j');
	g.InsertEdge('i', 'l');
	g.InsertEdge('j', 'm');
	g.InsertEdge('l', 'n');
	g.InsertEdge('l', 'm');
	g.InsertEdge('m', 'o');
	g.InsertEdge('m', 'p');
	g.InsertEdge('n', 'p');
	g.InsertEdge('o', 'q');
	g.InsertEdge('p', 'r');
	g.InsertEdge('q', 'r');
	g.ShowGraph();
	g.TopologicalSort();
}

对各顶点和边的添加:第一列是顶点,第二列是计算所有顶点的入度情况,第三列是邻接表

 

 

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

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

相关文章

详细介绍React生命周期和diffing算法

事件处理 1.通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —— 为了更好的兼容性&#xff1b;React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ——为了的高效。 2.通过event.target得到发生事件的DOM…

数据挖掘,计算机网络、操作系统刷题笔记54

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记54 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

搭建kafka集群

Kafka集群依赖ZK&#xff0c;需要先启动ZK集群 机器&#xff1a;hadop101,hadoop102, hadoop103 【1】在hadoop101解压&#xff1a; tar -zxvf kafka_2.12-2.4.1.tgz -C ../module/ 【2】在hadoop101修改server.properties配置&#xff1a; #指定broker的id&#xff0c;类似zk…

亚马逊云科技SageMaker:实现自动、可视化管理迭代

现如今&#xff0c;AI正在成为跨时代的技术&#xff0c;在数字经济发展中登上舞台&#xff0c;发挥关键作用。在Gartner发布的《2022年新兴技术成熟度曲线》*报告中&#xff0c;AIGC&#xff08;即AI Generated Content&#xff0c;人工智能自动生成内容&#xff09;被列为2022…

微搭使用笔记(四) 通过循环展示组件+json配置生成表单及数据获取

背景及整体思路 上篇文章我们通过微搭提供的数据模型完成了问卷表单页面的创建和数据采集&#xff0c;相对来说除了数据模型配置略显复杂外其他的倒还算方便。 本文我们通过for循环加上json文件配置的方式实现一个通用表单页面&#xff0c;如果更换了表单只需要替换掉json配置…

stm32 VM8978 音乐播放

一、WAV文件 1、WAV文件简介 2、WAV文件的解析 二、WM8978 1、WM8978介绍 2、WM8978特点 3、WM8978接口 4、WM8978框架 5、 WM8978 寄存器 三、IIS详解 1、IIS介绍 2、 IIS 的特点 3、IIS框架 4、 音频协议 5、 IIS Philips 标准 6、 IIS 时钟 四、音乐播放硬件…

力扣-删除重复的电子邮箱

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;196. 删除重复的电子邮箱二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其…

2.22Linux系统搭建

一.搭建Linux运行环境需要部署Java程序到服务器上,这样程序才能被外面的用户访问到1.安装jdkyum install develop x86_642.安装tomcat1)下载好,通过xshell直接拖到服务器上,依赖了rz命令2)解压缩unzip命令3)使.sh都有可执行权限chmod x *.sh4)启动 sh startup.sh5) 验证 ①ps a…

MAC配置pycharm

Mac配置pycharm 恢复出厂配置 # configuration rm -rf ~/library/preferences/JetBrains/ # caches rm -rf ~/library/caches/JetBrains/ # plugins rm -rf ~/library/application support/JetBrains/ # logs rm -rf ~/library/logs/JetBrains/文件头部 Python #!/usr/bin/e…

Mind+Python+Mediapipe项目——AI健身之跳绳

原文&#xff1a;MindPythonMediapipe项目——AI健身之跳绳 - DF创客社区 - 分享创造的喜悦 【项目背景】跳绳是一个很好的健身项目&#xff0c;为了获知所跳个数&#xff0c;有的跳绳上会有计数器。但这也只能跳完这后看到&#xff0c;能不能在跳的过程中就能看到&#xff0c;…

【Linux】virtualbox获取虚拟机串口日志方法,值得收藏

环境 宿主机&#xff1a;redhat 7.8 virtualbox &#xff1a;6.1.10 虚拟机&#xff1a;UOS 1050u1a x86 一、virtualbox设置 在串口栏中勾选 []启用串口 端口编号选择COM1 端口模式选择裸文件 Port/File Path: 填上 /tmp/box 也就是说我们在宿主机器的/tmp/中创建了vbox的…

C语言知识总结

" "和’ 的比较 " "视为字符串&#xff0c;且编译器在后面自动加上’\0’ 则视为单个字符&#xff0c;整型 1、本质区别 双引号里面的是字符串&#xff0c; 而单引号里面的代表字符。 2、输出区别 str “a”输出的就是a这个字母&#xff1b; str ‘a’…

GSON入门篇(内含教学视频+源代码)

GSON入门篇&#xff08;内含教学视频源代码&#xff09; 教学视频源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87474475 目录GSON入门篇&#xff08;内含教学视频源代码&#xff09;教学视频源代码下载链接地址&#xff1a;[https://d…

j6-IO流泛型集合多线程注解反射Socket

IO流 1 JDK API的使用 2 io简介 输入流用来读取in 输出流用来写出Out 在Java中&#xff0c;根据处理的数据单位不同&#xff0c;分为字节流和字符流 继承结构 java.io包&#xff1a; File 字节流&#xff1a;针对二进制文件 InputStream --FileInputStream --BufferedInputStre…

【数据结构与算法】字符串1:反转字符串I 反转字符串II 反转字符串里的单词 剑指offer(替换空格、左旋转字符串)

今日任务 344.反转字符串541.反转字符串II剑指Offer 05.替换空格151.反转字符串里的单词剑指Offer58-II.左旋转字符串 1.Leetcode344.反转字符串 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/reverse-string &#…

项目管理工具dhtmlxGantt甘特图入门教程(十一):后端集成问题解决方法

这篇文章给大家讲解如何解决dhtmlxGantt后端集成的问题。 dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足应用程序的所有需求&#xff0c;是完善的甘特图图表库 DhtmlxGantt正版试用下载https://www.evget.com/product/4213/download …

联想小新 Air-14 2019IML电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。硬件型号驱动情况主板Lenovo LNVNB161216处理器Intel Core i5-10210U / i7-10510U已驱动内存8GB DDR4 2666已驱动硬盘康佳KAK0500B128(128 GB/固志硬盘)已驱动显卡Intel UHD 620Nvidia GeForce MX250(屏蔽)无法驱动声卡Cone…

中国社科院与美国杜兰大学金融管理硕士——努力看到别样的风景

卡耐基曾说过&#xff0c;现在的努力是为了换取走更远的路&#xff0c;看到别人看不到的风景。现在卖命是为了让年老的时候&#xff0c;可以不用疲于奔命。对于这段话我深以为然&#xff0c;现在不努力&#xff0c;更待何时呢&#xff0c;就像在职的我们&#xff0c;想发展的更…

编译原理笔记(1)绪论

文章目录1.什么是编译2.编译系统的结构3.词法分析概述4.语法分析概述5.语义分析概述6.中间代码生成和后端概述1.什么是编译 编译的定义&#xff1a;将高级语言翻译成汇编语言或机器语言的过程。前者称为源语言&#xff0c;后者称为目标语言。 高级语言源程序的处理过程&#…

2020蓝桥杯真题回文日期 C语言/C++

题目描述 2020 年春节期间&#xff0c;有一个特殊的日期引起了大家的注意&#xff1a;2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202&#xff0c;恰好是一个回文数。我们称这样的日期是回文日期。 有人表示 20200202 是 “千年一遇…