【第二十一课】拓扑序列bfs (acwing-848有向图的拓扑序列 / c++代码 )

目录

拓扑序列

代码如下 

关于队列初始的解释 


拓扑序列

关于拓扑排序有几点:

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)


这次先写到这里~

有问题欢迎指出!一起加油!!

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值