拓扑排序 很简单

原文链接:图论第四讲:拓扑排序

说明:CSDN和公众号文章同步发布,需要第一时间收到最新内容,请关注公众号【比特正传】。

之前的图论合集文章中讲了图的存储遍历、最短路等算法,文章链接如下

图论第一讲:图的存储及遍历

图论第二讲:Dijkstra求最短路

图论第三讲:Bellman-ford与SPFA求最短路

今天来梳理一篇关于拓扑排序的文章。

0、其他相关概念

入度:在有向图中,指向自己的箭头的个数。

1、拓扑排序基本概念

      简单点说,在有向图中,任意一对相连节点u指向v,那么排序后u一定会在v的前面,得到这样的序列的算法称为拓扑排序。

上面的说法很简陋,但是也很容易理解,如果你去查百度百科,我相信对于大部分初学者来说,看完可能就退出这篇文章了。

2、问题描述

比如一道拓扑排序的模板题,先看题

理解了这道题的意思,就很容易理解拓扑排序是什么意思了。

题目链接:

https://www.luogu.com.cn/problem/B3644

3、算法描述

拓扑排序应该算是图论中比较简单的一个算法,首先我们先思考一下,在有向图中,如果当前一个节点的入度为0,则可以认为该节点是当前图中“辈分”最大的节点。那么对当前图拓扑排序的时候,一定会先输出该节点。

有了以上共识,我们可以借助入度来实现拓扑排序。

Step1:找出当前图论中未被标记的所有入度为0的节点,加入普通队列中,这些节点的顺序就已经排好了,可以当前输出,因此需要更新他们相邻节点的入度。

Step2:针对Step1中找出的节点,依次访问这些节点的相邻结点,并将其相邻结点的入度-1(相当于删除了当前入度为0的点,所以相邻结点的入度会-1),然后判断相邻结点的入度,如果-1后等于0,那么将其加入队列。

循环上面的步骤,直到队列为空即可。

4、code

看完算法描述,结合代码浏览更容易理解


#include "bits/stdc++.h"
using namespace std;
const int N = 1e5+7;
int n, m, u, v, in[N];
queue<int> q;
vector<int> g[N];

void topsort() {
  for(int i=1; i<=n; i++) {
    if(in[i] == 0) q.push(i);  // 初始的时候,找到所有入度为0的节点,加入队列 
  }
  while(!q.empty()) {
    int head = q.front(); q.pop(); // 取队首并弹出,一般都是同步操作 
    cout << head << " ";  // 输出序列中当前的节点 
    for(int x : g[head]) {  // 找与其相邻的其他结点 
      in[x]--;  // 入度-1 
      if(in[x] == 0) q.push(x);  // 如果相邻结点的入度为0,那么加入队列 
    }
  }
  return;
}

int main(){
  scanf("%d", &n);
  for(u=1; u<=n; ++u) {
    while(true) {
      scanf("%d", &v);
      if(v == 0) break;  // 根据题目意思,遇到0停止 
      g[u].push_back(v);
      in[v]++;  // 统计入度 
    }
  }
  topsort();
  return 0;
}

这道模板题呀,很简单,由于题目没有说,因此不需要考虑一些异常情况,比如图的连通性,如果在拓扑排序中有冲突怎么办等等,因此我们只需要考虑它的正常情况就行了,非常适合新手练习拓扑排序,自己动手完成吧。

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值