目录
拓扑序列
关于拓扑排序有几点:
1.拓扑序列中,每条有向边都是从序列中前面的顶点指向后面的顶点。
2.有向无环图(DAG)一定有拓扑序列。存在环的图一定没有拓扑序列,因为环必定有从后面的点指向前面的点的边。
3.一个有向无环图一定至少有一个入度为0的点。
4.拓扑排序有多种可能的序列,因为图中可能存在多个入度为零的顶点,而这些顶点可以以任意顺序加入拓扑序列。
如何求拓扑排序?(刚死去的数据结构知识来攻击我了hh
下面这个视频讲了求拓扑排序的方法,推荐看
求拓扑排序的步骤就是:找到入度为0的顶点,删掉由该顶点指出的所有边,之后,在剩余的顶点中再次找到入度为0的顶点......重复该操作。
而我们代码实现就是,用队列模拟这个过程。
代码如下
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int h[N],e[N],ne[N],idx;
int q[N],d[N];//d数组此时表示的是每个顶点的入度
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool topsort()
{
int hh=0,tt=-1;//表示队头 队尾的指针 tt初始化为-1 队列中的元素下标从0开始
for(int i=1;i<=n;i++)//顶点用1~n表示
{
if(!d[i])q[++tt]=i;//入度为0就入队
//++tt tt始终表示的是当前队列的最后一个元素
}
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i])//遍历邻接顶点
{
int j=e[i];
d[j]--;
if(d[j]==0)q[++tt]=j;//删除顶点之后,若有顶点由此入度变为0,入队
}
}
return tt==n-1;//当队列尾部的计数与顶点数相同时说明找到了一个拓扑排序
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);//初始化邻接表的顶点集
for(int i=0;i<m;i++)
{
int x,y;
cin>>x>>y;
add(x,y);
d[y]++;//边由x指向y,y的入度要++
}
if(topsort())
{
//数组模拟队列只是定义了队头队尾指针,通过这两个指针的移动来划定队列范围,然鹅在q数组中其实是包含所有插入的元素的。刚好我们元素插入的顺序就是拓扑序列
for(int i=0;i<n;i++)
{
cout<<q[i]<<" ";
}
}
else cout<<"-1"<<endl;
return 0;
}
关于队列初始的解释
写完代码之后,我发现这里队列的初始值是
int hh=0,tt=-1;
在拓扑排序这道题里,tt 是在每次有新的元素加入队列时才递增,它始终表示的是队列中最后一个元素的位置。这段代码用来执行拓扑排序,所以只有当一个顶点的入度为0时,它才会被加入到队列中。
我们之前写的两道题队列的初始值:
走迷宫:
int hh=0,tt=0;//定义队头和队尾
q[0]={0,0};//将起始点存入队列
这道题的代码是要得到从迷宫左上走到右下所需的最少移动次数,左上{0,0}就是一个指定的起始顶点开始。因此需要进行初始化q[0]
计算图中点的层次:
int hh=0,tt=0;
q[0]=1;//将顶点1放入队列作为起始顶点(可以从任何一个顶点开始进行广度优先搜索)
队列的第一个元素在 bfs() 函数开始时就已经被设定好了,这道题的代码是用来执行广度优先搜索得到最短距离的,所以需要从一个指定的起始顶点开始。就初始化了q[0]
这个细节要注意一下。
需要再看一下数组模拟xx的可以回顾一下
【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)
这次先写到这里~
有问题欢迎指出!一起加油!!