目录

  • 背景
  • 实现思路
  • 代码

背景

在学习拓扑排序的时候,老师提出了一个问题:在逆拓扑排序算法中如何识别出回路?
总所周知,拓扑排序必须在DAG(有向无环图)中实现,也就是说如果给定的图带有回路,就无法进行拓扑排序。
我经过思考,想出了一个识别方法,在此做出记录。

实现思路

何为回路,我的理解就是访问回头路,从系统栈的角度说,就是指向了还在栈中的节点。只要节点还在栈中节点就还在访问路径上,此时指向就是走回头路。若节点不在栈中,出栈了,访问路径自然也就把改点剔除,就不存在经过该的回路。


我们是基于DFS算法进行拓扑排序。DFS是基于递归实现的算法,从初始点开始,依次遍历第一个邻居节点,直到没有邻居为止,递归结束时输出节点,输出的顺序就是拓扑排序。
以上图为例,就是从0开始依次遍历1、3、4;因为4没有指向其他节点。递归结束。然后搜索剩余未遍历的点,再次进行递归遍历。
从系统栈的角度就是,依次将0、1、3、4压入栈。递归结束,输出4、3、1、0;第二轮递归把2压入,输出2。

DFS算法,每轮递归访问一次最深的路径,一共两轮,最终的排序次序为:4、3、1、0、2

如果没有回路,那么每一轮递归只会沿着路径做最深访问,不会出现重复(从系统栈的角度,指向了还在栈中的节点)。
比如第一张图的0、1、3、4没有出现重复访问,但第二张图中,0、1、3、4、2、3,在同一轮递归中访问了重复的节点3。说明出现了回路(走了回头路)

因此我们可以添加一个数组nowVisited[],记录每一轮递归节点被访问的情况。这样就有两个数组记录节点访问情况,visited[]记录全局访问情况,nowVisited[]只记录本轮递归的访问情况。
如果一个节点在本轮递归中没有访问,说明没有出现回路,继续执行代码。
如果一个节点在本轮递归被访问过了,说明出现了回路,代码立即中止执行。

代码

#include <iostream>
using namespace std;
//定义邻接矩阵
#define MAXVERTEXNUM 100
typedef struct {int vex[MAXVERTEXNUM];int edge[MAXVERTEXNUM][MAXVERTEXNUM];int vexnum, arcnum;
}Graph;
//寻找第一个邻接点
int FirstNeighbor(Graph G, int v)
{for (int i = 0; i < G.vexnum; ++i){if (G.edge[v][i] != 0){return i;}}return -1;
}
//寻找下一个邻接点
int NextNeighor(Graph G, int v, int w)
{for (int i = w+1; i < G.vexnum; ++i){if (G.edge[v][i] != 0){return i;}}return -1;
}
//DFS遍历找到排序次序
bool visited[MAXVERTEXNUM];
bool nowVisited[MAXVERTEXNUM];
bool warry = false;
void DFS(Graph G, int v) {visited[v] = true;//记录全局访问情况nowVisited[v] = true;//记录本轮访问情况for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighor(G, v, w)) {//依次递归访问v的邻居节点if (nowVisited[w] == true) {//如果本轮已经访问过了v的邻居节点w,出现回路,立即中止代码cout<<"\n出现回路:"<< G.vex[v] <<"=>"<< G.vex[w];exit(0);}if (visited[w] == false) {//没被访问就继续递归,沿着该点路径继续延长DFS(G, w);}}//forcout << G.vex[v] << "<-";nowVisited[v] = false;//本轮结束,消去本轮对应的访问记录。
}
//防止遗漏
void DFSTraverse(Graph G) {for (int v = 0; v < G.vexnum; ++v) {//初始化数组。visited[v] = false;nowVisited[v] = false;}for (int v = 0; v < G.vexnum; ++v)//防止出现遗漏if (visited[v]==false)DFS(G, v);
}
//测试代码
void test()
{//初始化图Graph G;G.arcnum = 5;G.vexnum = 5;//记录节点的信息(id、权重等等),如邻接矩阵中0可以对应"a"节点或者"1"节点for (int i = 0; i <=G.arcnum; ++i)G.vex[i] = i+1;//这里邻接矩阵用0、1、2、3代表节点,以便适应数组下标for (int i = 0; i <= G.arcnum; ++i){for (int j = 0; j <= G.arcnum; ++j){G.edge[i][j] = 0;}}//设置连接的有向边G.edge[0][1] = 1;G.edge[1][2] = 1;G.edge[2][3] = 1;G.edge[4][1] = 1;G.edge[2][4] = 1;G.edge[4][3] = 1;//执行逆排序DFSTraverse(G);
}
int main()
{test();return 0;
}

新开通了本人的公众号,欢迎关注:燕南路GISer ,专注GIS干货分享,不定期更新。
主要兴趣:GIS、时空数据挖掘、python、机器学习深度学习
CSDN的部分内容会重写再搬迁到公众号,欢迎关注!

图的逆拓扑排序(回路识别)相关推荐

  1. 图——深度优先遍历(DFS)实现有向无环图的逆拓扑排序

    对图的深度遍历与对树的深度遍历思想类似,采用的是递归函数的办法. 如果是非连通图,则DFS遍历所有顶点即可. //Graph 图 //vertex 顶点,用一个int型变量表示//返回有向图G中顶点v ...

  2. 拓扑排序及逆拓扑排序

    拓扑排序其实就是对有向无环图的顶点的一种排序,每个顶点出现且只出现一次. 对一个AOV网进行拓扑排序的方法: 1.从AOV网中选择一个入度为0的顶点并输出: 2.从网中删除该顶点和所有以它为起点的有向 ...

  3. DFS实现逆拓扑排序

    多思考递归的过程! //DFS实现逆拓扑排序 bool visited[MaxVertexNum]; void DFSTraverse(Graph G){for(v=0;v<G.vexnum;v ...

  4. 图论--拓扑排序--判断一个图能否被拓扑排序

    拓扑排序的实现条件,以及结合应用场景,我们都能得到拓扑排序适用于DAG图(Directed Acyclic Graph简称DAG)有向无环图, 根据关系我们能得到一个线性序列,实现的方式是DFS,具体 ...

  5. java 有向无环图 树_拓扑排序-有向无环图(DAG, Directed Acyclic Graph)

    条件: 1.每个顶点出现且只出现一次. 2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面. 有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说. 一 ...

  6. 有向无环图的所有拓扑排序

    有向无环图的所有拓扑排序 对有向无环图DAG的拓扑排序是顶点的线性排序,从而使每一有向边[u,v][u,v][u,v],顶点u进来的顺序v在.如果图不是 DAG,则无法对图进行拓扑排序. 给定一个 D ...

  7. 【数据结构-图】4.拓扑排序和关键路径(注解+原理)

    一.拓扑排序 1.1 基本知识 有向无环图:一个有向图中不存在环,简称DAG图 AOV网:用DAG图表示一个工程,其顶点表示活动,用有向边 <Vi,Vj><V_i, V_j>& ...

  8. 数据结构——AOV图与算法——拓扑排序

    AOV图:以有向图中的顶点来表示活动,以有向边来表示活动之间的先后次序关系. 拓扑排序:对一个有向无环图(AOV)进行活动先后的排序方法 拓扑排序思路: 1.统计所有节点的入度 2.把所有入度为0的节 ...

  9. 有向无环图——AOV网(拓扑排序)

    有向无环图:无环的有向图,简称DAG图(Directed Acycline Graph) 有向无环图常用来描述一个工程或系统的进行过程.(通常吧计划.施工.生产.程序流程等当成是一个工程) 一个工程可 ...

最新文章

  1. Golang之轻松化解defer的温柔陷阱
  2. Oracle中常用的命令,随着学习进度总结
  3. Django之URL路由系统
  4. Django 02 url路由配置及渲染方式
  5. mysql默认安装目录说明
  6. voip 音频采集时间_蓝牙音频续航监测系统展会现场演示
  7. 我使用的几个Linux终端使用技巧
  8. Android自定义柱状图表效果
  9. 手把手教你入侵网站修改数据_手把手教你使用Python抓取QQ音乐数据(第四弹)...
  10. python的numpy入门简介
  11. 自写的简单屏蔽特定字符的TextBox和数字TextBox
  12. 学习Linux必备的硬件基础一网打尽
  13. 拍照怎么搜题?(上)
  14. 树莓派 串口如何使用 以及树莓派引脚对照表
  15. Python学习笔记—— 面向对象1. 面向对象基础
  16. 编程实现幂函数,(指数为整数)
  17. 腾讯面试总结——iOS开发
  18. 在写C语言函数时什么时候需要函数声明
  19. 分号的html文本,在Vue中利用v-HTML按分号将文本换行的例子_輕微_前端开发者
  20. 海思hi3519a的MIPI用法

热门文章

  1. php版本支持存储过程,PHP升级到4.3版本之后改变了调用存储过程的一个特性
  2. 生存战争-中阶模拟量电路板视频学习记录
  3. S3C6410中断分类
  4. 论——大专生就只能进入电子厂吗?
  5. 如何对PDF文献做可视化分析?
  6. crontab自定义脚本执行失败,但是手动执行是成功的
  7. 系统里有哪些软中断?
  8. 南方iData数据工厂(一个平台,一套数码,一体化生产):构建基础空间数据的好平台
  9. 一年催回超40亿,催收巨头永雄上市梦仍在
  10. 巡检机器人中的指针式仪表读数识别系统