数据结构:图的拓扑排序与关键路径

目录

一、拓扑排序

1.1、算法的基本步骤

1.2、算法实现

1.4、习题思考

1.5、DFS生成逆拓扑序


一、拓扑排序

43dff1fbc5604feeaecc9992b4c901c4.png

        AOV网:有向图中, 顶点表示活动(或任务), 有向边表示活动(或任务)间的先后关系,称这样的有向图为AOV网(Activity On Vertex Network)。
        AOV网络中不能出现有向回路,即有向环。
         拓扑序列:就是把AOV网中的所有顶点排成一个线性序列,若AOV网中存在有向边 Vi Vj ,则在该序列中, Vi 必位于 Vj 之前。在拓扑序列中,先进行的任务一定在后进行的任务的前面。按照拓扑序列完成各子任务,就可以顺利完成整个任务。拓扑序列未必唯一。
如果不能排成一个拓扑序列,则说明AOV网络中存在有向环。

1.1、算法的基本步骤

①从图中选择一个入度为0的顶点并输出。

②从图中删除该顶点及该顶点引出的所有边。

③执行①②,直至所有顶点已输出,或剩余顶点入度均不为0(说明存在环,无法继续拓扑排序)

时间复杂度O(n+e):计算入度O(n+e),删除顶点O(n+e)。

1.2、算法实现

        实际上我们并不需要真正的删除该顶点,我们只需要记录每个顶点的入度,在“删除”某个顶点之后,它所邻接到的所有顶点的入度都减1即可。并且我们用栈保存入度为0的顶点,以供删除。课本上用数组实现栈,这里直接采用STL:stack。

#include<bits/stdc++.h>
using namespace std;
#define n 10
struct Edge{int VerName;int cost;Edge * next;
};
struct Vertex{int VerName;Edge * edge=nullptr;
};
vector<int> cnt(n);
Vertex Head[n];
vector<int> path;
void TopoOrder(){//进行拓扑排序,并判断是否存在环路path.clear();cnt.assign(n,0);for(auto & i : Head){//计算入度for(Edge * edge=i.edge;edge!=nullptr;edge=edge->next){cnt[edge->VerName]+=1;//入度++}}stack<int> sta;for(int i=0;i<n;++i) if(!cnt[i]) sta.push(i);//初始化入度为0的家伙int num=0;while(!sta.empty()){//拓扑排序int cur=sta.top();sta.pop();path.emplace_back(cur);//路径存入for(Edge * edge=Head[cur].edge;edge!=nullptr;edge=edge->next){//删除点cnt[edge->VerName]-=1;//入度++if(cnt[edge->VerName]==0) sta.push(edge->VerName);}}if(path.size()!=n) printf("有向图中存在环路");else for(auto i:path) printf("%d ",i);return;
}int main(void){return 0;
}

        由于一个顶点的入度被删减为0时,不可能再有边指向该顶点,因此该顶点不会被再次访问,则其cnt[i]空闲了。即当cnt[i]==0时,cnt[i]就不会被访问了,i也会入栈,因此我们可以利用这个空闲直接在cnt上实现栈。

优化用cnt数组空闲空间实现栈(静态链表):

int top=-1;//模拟栈顶
for(int i=0;i<n;++i){if(cnt[i]==0){cnt[i]=top;//指向以前的栈顶top=i;//栈顶变为i}
}while(top!=-1){int cur=top;path.emplace_back(cur);top=cnt[top];//静态链表,相当于弹栈for(Edge * edge=Head[cur].edge;edge!=nullptr;edge=edge->next){//删除点cnt[edge->VerName]-=1;//入度++if(cnt[edge->VerName]==0) {cnt[edge->VerName]=top;top=edge->VerName;}}
}

1.4、习题思考

(1)给定一个图和顶点序列,编写算法判断该序列是否是图的拓扑序列。

只需要先计算一次入度,然后扫描所给定的顶点序列,对于每一个顶点:

①先判断该顶点入度是否为0,如果不为0,则说明不是拓扑序,如果为0,则进行②

②将该顶点的边删除(即为其邻接顶点减少入度),之后继续扫描。

        实际上我们假定给定的顶点序列是拓扑序列,则按照该顶点序列选择顶点删除边,选择的顶点一定是入度为0的。

(2)802. 找到最终的安全状态

60caf4bc64614950b536e8b55f93d644.png

        如果本题正着想,则从一个点开始DFS遍历,如果它能遍历到终端结点,则路径上的所有点都能遍历到终端结点,如果它的所有邻接结点都是安全结点,则它也将是安全结点,如果它有一个邻接结点不是安全结点则必然它也不是安全结点(换句话说如果存在环路,则遍历到一个结点不是安全结点且已经被遍历过,则这条路并不通往终端节点,则它不是安全结点)。然后继续寻找下一个不是安全结点的结点且未被遍历过的结点开始遍历。<不是安全结点的结点且被遍历过的结点一定不可能是安全结点了,因为如果它是,那么它的所有邻接结点都是安全结点。后根DFS也将判断它为安全结点。>


        如果说你能走到终端结点,你才算是安全结点。那么反过来走,只能被终端节点才能走到的结点才是安全结点。考察反向图,终端节点是入度为0的顶点,进行拓扑排序,排序过程中如果入度为0则被认为是安全结点(证明:开始时终端结点为安全结点,入度为0,删除其所有出边,剩下的结点入度为0的则说明它们只能被终端结点走到,它们是安全结点,然后把它们当作终端结点继续执行同样的操作,以此类推)。

class Solution {
public:vector<int> eventualSafeNodes(vector<vector<int>>& graph) {int n=graph.size();vector<vector<int>> gra(n,vector<int>{});vector<int> cnt(n);stack<int> sta;vector<int> ans;for(int i=0;i<n;++i){cnt.at(i)=graph.at(i).size();if(!cnt.at(i)){sta.push(i);}for(int j=0;j<cnt.at(i);++j){gra.at(graph.at(i).at(j)).emplace_back(i);}}while(!sta.empty()){ans.push_back(sta.top());sta.pop();for(auto i:gra[ans.back()]){cnt.at(i)--;if(cnt.at(i)==0) sta.push(i);}}sort(ans.begin(),ans.end());return ans;}
};
//什么STL大王,在这全程at(),看得脑袋晕,一般还是别用了(。。),用起来自己看不明白呀,除非你想测试越界

1.5、DFS生成逆拓扑序

为什么DFS能生成逆拓扑序?

        我们来思考一下拓扑序列的原始定义。在一个拓扑序列中,如果vi在vj之前,则必然在有向图中有vi→vj。那么如果我们使用DFS“后根遍历”,则对于任意的vi→vj,一定有vi在vj之后输出,换句话说,对于有向图中的每一条边vi→vj均满足,vi在vj之后,如果将这一输出序列反转,则等价于对于有向图中的每一条边vi→vj均满足,vi在vj之前。相当于对于任何一个顶点vi,其后继邻接顶点都会在其之前被输出,否则它不会被输出。反过来就是,只有当vi被输出之后,其邻接顶点才会被输出,即满足拓扑排序。因此DFS“后根输出”为拓扑排序的逆序。

        当然你得先保证这是一个AOV网络,不然DFS就变成了纯遍历了。

int vis[n];
void DFS_TopoOrder(int root){vis[root]=1;for(Edge * edge=Head[root].edge;edge!=nullptr;edge=edge->next){if(vis[root]==0)DFS_TopoOrder(edge->VerName);}printf("%d ",root);return;
}
for(int i=0;i<n;++i)if(cnt[i]==0) DFS_TopoOrder(i);//从入度为0的开始遍历

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑msvcp140_1.dll丢失的解决方法,总结5种可靠的方法

在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp1401.dll丢失”。这个DLL文件是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于Windows的应用程序来说至关重要。这个错误通常会导致某些应用程序无…

静态综合实验

一&#xff0c;1.搭建拓扑结构并启动。 2.根据题意得该图需要14个网段&#xff0c;根据192.168.1.0/24划分子网段&#xff0c;如下&#xff1a; 划分完如图所示&#xff1a; 二、配置IP地址 R1路由器&#xff1a; 1.进入系统视图并改名. 2.接口配置IP地址&#xff1a…

代码随想录算法训练营第二十七天(第二十六天休息)|40.组合总和II

40.组合总和II 题目 给定一个数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。 说明&#xff1a; 所有数字&#xff08;包括目标数&#xff09;都是正整数。解集…

【Unity】CatlikeCoding SRP

Unity 自定义渲染管线 提示&#xff1a;基于CatlikeCoding SRP系列教程学习 学习链接&#xff1a;SRP 个人测试: Demo 相关记录以后有时间再更&#xff1a;

vivado 物理优化、物理端口设计

物理优化消息 提示&#xff1a;物理优化报告为优化处理的每个网络&#xff0c;以及优化摘要执行&#xff08;如有&#xff09;。如下图所示&#xff0c;在物理优化结束时提供了一个摘要显示了每个优化阶段的统计数据及其对设计性能的影响。这突出显示了对改进WNS最有效的优化类…

解决nginx报错nginx: [emerg] unknown log format main in 的方法

目录 一、故障描述1&#xff1a; 重启nginx是出现了如下错误 解决办法 二、故障描述2&#xff1a; 解决办法&#xff1a; 三、nginx介绍​ 四、nginx原理 五、nginx.conf配置文件 六、nginx负载均衡 七、正向代理、反向代理 一、故障描述1&#xff1a; 在添加Nginx的…

JS的一些方便遍历数组的API函数

这些函数有的时候在学到后面的内容会遇到&#xff0c;看一些前端的视频的时候突然出现这些函数看到有点懵&#xff0c;现在就整合一下&#xff0c;然后以后看到这些函数就知道是干什么的了 1、箭头函数 没学完js的必须得先了解箭头函数 //它两一样 function fn(){console.lo…

LeetCode刷题记录:(11)组合(初识回溯算法)

leetcode传送通道 暂时记录&#xff0c;这篇没啥营养&#xff0c;不用看了 class Solution {List<List<Integer>> result new ArrayList<>(); // 存所有组合List<Integer> path new LinkedList<>(); //存每一个组合public List<List<Int…

springboot 动漫周边商城的设计与实现

摘 要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一…

社区维修平台|基于SpringBoot+ Mysql+Java+JSP技术的社区维修平台设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 住户后台功能 维修员前台功能 维修员后台功能 管理员功能登录 系统功能设计 数据库E…

代码随想录刷题day27|组合总和II组合总和II分割回文串

文章目录 day27学习内容一、组合总和-所选数字可重复1.1、代码-正确写法1.1.1、为什么递归取的是i而不是i1呢&#xff1f; 二、组合总和II-所选数字不可重复2.1、和39题有什么不同2.2、思路2.2.1、初始化2.2.2、主要步骤2.2.3、回溯函数 backTracking 2.3、正确写法12.3.1、为什…

什么是CPU?CPU的性能指标是什么?

我们在就看一台笔记本电脑配置时&#xff0c;必然要关注CPU的型号与性能&#xff0c;那么你知道什么是CPU吗&#xff1f;CPU的性能指标又是什么呢&#xff1f;如何来衡量这款CPU的性能是不是很强大&#xff1f;我们来一起看一下&#xff01; 什么是CPU CPU&#xff0c;全称中央…