(数据结构)有向图的拓扑排序 Kahn算法

拓扑排序是对有向无圈图的顶点的一种排序,使得如果存在一条从vi到vj的路径,那么在排序中,vi必须出现在vj的前面

首先,我们需要准备一个vector<int> link[maxn],link[i] 存放顶点i所有连接到的顶点,同时还要维护一个队列或者栈来存放所有入度为0的顶点。

下面,直接上图:

假如有这样一个有向图:
在这里插入图片描述

  1. 我们发现只有顶点1的入度为0,所以把顶点1给去掉,并把1加入结果数组。res=[1]
    在这里插入图片描述
  2. 目前就还剩顶点2和顶点5的入度为0,随机去掉一个,我们去掉2,并把2加入res数组res=[1,2]

在这里插入图片描述
3. 目前顶点5的入度为0,把顶点5给去掉。res=[1,2,5]
在这里插入图片描述
4. 目前顶点4的入度为0,把顶点4给去掉。res=[1,2,5,4]
在这里插入图片描述
5. 目前顶点3的入度为0,把顶点3给去掉。res=[1,2,5,4,3]

直到最后不剩下顶点时,排序结束,1 2 5 4 3即为该图的一个拓扑排序。

细节:

  1. 拓扑排序的结果并不唯一,比如在上面第二步,我们先去掉的是顶点2,如果我们先去掉的是顶点5,那么拓扑排序的结果可能是1 5 4 3 2
  2. 假如图中有环,比如下图,那么所有的顶点的入度都不为0,排序无法进行下去,拓扑排序将不存在。所以可以通过拓扑排序来判断图中有没有环。
    在这里插入图片描述

代码如下:

// Kahn算法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1024
vector<int> link[maxn];   //顶点i的邻接表
queue<int> q;  //存放所有入度为0的顶点
int in[maxn];   //顶点i的入度
int n;  //顶点个数
int m;  //边的个数
int res[maxn];   //存放排序后的结果

/*
新增一条由n1指向n2的有向边
*/
void lnk(int n1,int n2){
    link[n1].push_back(n2);   //n1顶点的邻接表加入n2
    ++in[n2];                 //n2的入度+1
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    //建图
	n = 13;
    m = 15;
    lnk(1,2);
    lnk(1,6);
    lnk(1,7);
    lnk(3,1);
    lnk(3,4);
    lnk(4,6);
    lnk(6,5);
    lnk(7,4);
    lnk(7,10);
    lnk(8,7);
    lnk(9,8);
    lnk(10,11);
    lnk(10,12);
    lnk(10,13);
    lnk(12,13);
    //扫描in数组,将入度为0的顶点加入队列
    for(int i = 1;i <= n;++i){
        if(!in[i]){
            q.push(i);
        }
    }

    int index = 0;   //res的下标
    while(!q.empty()){
        int node = q.front();
        q.pop();    //拿出一个结点
        res[index++] = node; //存入结果数组
        //逐一移除跟它相连的边
        for(auto it = link[node].begin();it != link[node].end();++it){
            --in[*it];   //顶点入度-1
            if(!in[*it]){
                //如果入度为0,加入队列
                q.push(*it);
            }
        }
    }
    //如果index<n,那么加入结果数组的顶点数量一定是小于n的
    //说明该图有环
    if(index == n){
        for(int i = 0;i<n;++i){
            cout << res[i] << " \n"[i == n-1];
        }
    }else{
        cout << "circulation" << endl;
    }

	return 0;
}

练习:LeetCode No207. 课程表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值