当前位置: 首页 > news >正文

拓扑排序深入

拓扑排序

文章目录

  • 拓扑排序
    • 一、前言
    • 二、拓扑排序
      • 2.1 导入
      • 2.2 拓扑排序
        • 2.2.1 定义
        • 2.2.2 思想
        • 2.2.3 步骤
        • 2.2.4 图示模拟
        • 2.2.5 代码
    • 三、小结

一、前言

今天带来图的另外一种算法——拓扑排序。可是这和我们常见的排序一样吗?在中有怎样的新的表达呢?

二、拓扑排序

在图中的拓扑排序当然和线性结构的排序(冒泡/快速/交换等)完全不同。

它的存在受制于图,也被图赋予了特殊的含义,在现实生活中也表现出独特的性质。

比如说,在工程中,必须先有需求分析,才能制作功能流程图,其次设计架构,最后才可以进行项目开发。每件事情并非独立,而是具有一定的先后次序的;还有在闯关游戏中,必须通过当前关卡的游戏才能解锁下一关一样,关卡的设置和游戏的难易程度相关)。

拓扑排序的出现就是为了找到一种合理的完成事情的顺序

当然这只是拓扑排序的物理含义,抽象成模型是怎样的呢?

2.1 导入

2.1.1 有向无环图

拓扑排序的抽象模型就是有向无环图。

相信经过图的概述一篇,你肯定已经明白了有向无环图的定义。

放个图。

在这里插入图片描述

为什么一定是有向无环图呢?

使事情具有先后顺序(优先级),这不就是有向图吗?为什么是无环?这是为了保证顺序的可行性,防止形成死锁。

那如何判断一个图有没有环呢?

K r u s k a l Kruskal Kruskal​算法中,我们曾使用并查集来排除成环的可能性

在这里有了一种新的方式。(先卖个关子~)

2.1.2 活动

每一个小步骤就是一个活动(在图中就是顶点)。

2.1.3 A O V AOV AOV​网

A c t i v i t y Activity Activity O n On On V e r t e x Vertex Vertex N e t w o r k Network Network,在一个图中,顶点表示活动,弧(有方向的边)表示活动的优先关系,称为 A O V AOV AOV网。

活动的优先关系:弧尾的活动完成之后,才能去完成弧头的活动。

2.1.4 拓扑序列

一个路径中,有顶点在前,有顶点在后,从而形成的有顺序的顶点排列就是拓扑序列。

2.2 拓扑排序

2.2.1 定义

对一个有向无环图进行顶点的序列化处理

2.2.2 思想

对于一组有先后依赖关系的任务(或节点),找到一个线性的执行序列,使得任何任务都不会在其所依赖的所有前置任务完成之前开始

如图

在这里插入图片描述

阐释

一眼看过去。只有0没有依赖项,于是从0开始。0任务结束后,1,3都可以进行了。2为什么不行呢?2可是有两个依赖指向呢——0和1,必须0和1都完成才可以进行任务2。就这样,依次类推。

2.2.3 步骤

所谓依赖就是入度,因此拓扑排序核心为维护入度数组

  • 找到图中,入度为0的顶点,把这些顶点放入缓存区(栈/队列)

    一开始,一定有一个入度为0的顶点,是整个任务的伊始。

    这里的缓存区,我们采用栈。栈只需维护一个栈顶指针,但是队列需要维护队首和队尾两个指针。

  • 从缓存区中取出一个顶点,放入结果集,这个顶点的任务执行完毕

  • 顶点的出度对应的其余顶点的入度清零——解放依赖

  • 在清零的过程中,查找入度为0的顶点(两个事同时进行),如果有,继续放入缓存区(推进任务进度)

  • 重复2,3,4

  • 缓存区为空时,结果集中的顶点个数和实际图中顶点的个数,如果相等(任务都顺利完成啦),则没有环(判断成环的方法)

2.2.4 图示模拟

既然是有向图,存储结构首选邻接表(这里是正邻接表)

在这里插入图片描述

既然要找入度为0的顶点,不能一遍又一遍地遍历邻接表,因此,不如只遍历一遍得到一个入度记录表,当想找入度为0的点时,只需遍历一遍入度记录表更好(可以用优先级队列),这样效率更高一些。

在这里插入图片描述

栈结构

在这里插入图片描述
模拟:

  • 找到入度为0的点,入栈

在这里插入图片描述

  • 出栈

在这里插入图片描述

  • 清零入度表

在这里插入图片描述

  • 继续入栈(和上一步是同时进行的)
    在这里插入图片描述
  • 循环上述操作
2.2.5 代码

拓扑排序基于邻接表实现,先前已经实现了邻接表,在这里不再赘述了,直接采用其接口,详情见邻接表

// 拓扑排序,返回1表示有向无环,0表示无环,-1表示错误
int TopologicalSortAGraph(const AGraph *adjacencyList)	// 排序无需改变邻接表-const
{// 准备工作// 定义一个入度记录表,便于发现入度为0时,放入缓存区int *inDegree;inDegree = malloc(sizeof(int) * graph->nodeNum);if(inDegree == NULL){return -1;}memset(inDegree, 0, sizeof(int) * graph->nodeNum);// 初始化入度记录表// 遍历邻接表for(int i = 0; i < graph->nodeNum; i++){if(graph->nodes[i].firstEdge)			// 说明i节点开始有边{ArcEdge *edge = graph->nodes[i].firstEdge;while(edge){++inDegree[edge->no];edge = edge->next;}}}// 查找入度记录表,度为0的顶点,设计一个链式栈的缓存区,更新入度记录表时,发现0,就放入任务栈// 设计栈int *stack = malloc(sizeof(int) * graph->nodeNum);if(stack == NULL){free(inDegree);return -1;}int top = -1;// 入栈for(int i = 0; i < graph->nodeNum; ++i){if(inDegree[i] == 0){stack[++top] = i;}}// 根据任务栈里的数据,弹出第一个任务,这个任务对应的节点相关的入边个数删除// 更新任务记录表,如果更新过程中,发现入度为0,直接入栈int index;while(top != -1){index = stack[top--];count++;visitAGraphNode(&graph->nodes[index]);// 更新入度信息ArcEdge *edge = graph->nodes[index];while(edge){--inDegree[edge->no];if(inDegree[edge->no] == 0){stack[++top] = edge->no;}edge = edge->next;}}// 释放free(inDegree);free(stack);// 判断是否有环if(count == graph->nodeNum){return 0;}else{return 1;}
}

三、小结

所谓拓扑排序,不过是将生活中的事件抽象成一个物理模型,从而解决更多复杂的问题——关于将大事件拆分成小活动(步骤)完成,找到小活动的优先顺序,从而更加高效地解决问题。

这为我们的生活提供了 不少启示,不是吗?

下一篇,我将介绍一种图的新的应用,让我们一起揭开图更多的秘密吧~

http://www.dtcms.com/a/582414.html

相关文章:

  • docker部署kafka
  • 【镜中异客:AI与人类的禁忌之舞】
  • 微信网站模版下载新闻类网站源码
  • 手机网站滑动效果深圳一公司今年成立16家核检机构
  • 面向强化学习的状态空间建模:RSSM和PyTorch(3)
  • #Prometheus 权威指南:云原生监控的技术架构与实践精髓
  • Android11-Launcher3 定制-去除副屏幕-可以滑动效果
  • 通风管道部件-图形识别更快捷
  • 黄浦网站制作那个网站可以做雪花特效
  • 万网站底部添加备案号wordpress如何更换主机
  • MongoDB 与 Java 实体类型 LocalTime 时区转换问题解决方案
  • Linux 文件软硬链接详解
  • 青海城乡和住房建设厅网站后台更改公司网站背景图片
  • 烟台营销型网站建设怎么做网站的学校的大图
  • 随笔-随便写了
  • IEC61850 标准分析(第三部分)
  • Zabbix7添加监控主机
  • 刷赞网站推广qq免费福州专业网站设计
  • 国内购物网站案例分析寻花问柳专注做一家男性喜欢的网站
  • 模型理解与可解释性图表案例解读
  • 网站备份数据库白鹭引擎可以做网站吗
  • 微信小程序:onReady详解
  • 使用docker-compose启动springboot
  • 推销别人做网站有什么作用最近七天的新闻大事
  • A模块 系统与网络安全 第四门课 弹性交换网络-5
  • 做企业网站要用什么软件深圳商城网站设计费用
  • 网站备案名称重复wordpress如何安装模板文件夹
  • 如何解析和测试JSON/XML格式的响应?
  • 网站最上面标题怎么改做网站的为什么不给域名和密码
  • MySQL B+树