服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++详细讲解图的拓扑排序

C++详细讲解图的拓扑排序

2022-12-13 13:56quicklsleap C/C++

拓扑排序(Topological Sorting)若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列

一、前言

且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 x到顶点 y的路径,那么在序列中顶点 x 出现在顶点 y的前面。

拓扑排序只适用于 AOV网 (有向无环图)

若图中有环,则一定不存在拓扑序。

可以证明,一个有向无环图,一定存在一个拓扑序列。有向无环图,又被称为拓扑图。

入度: 即有多少条边指向自己这个节点。

出度: 即有多少条边从自己这个节点指出去。

二、算法流程

算法流程:

用队列来执行 ,初始化所有入度为0的顶点入队。

主要由以下两步循环执行,直到不存在入度为 0 的顶点为止

选择一个入度为 0 的顶点,并将它输出;

删除图中从顶点连出的所有边

循环结束

若输出的顶点数小于图中的顶点数,则表示该图存在回路,即无法拓扑排序,

否则,输出的就是拓扑序列 (不唯一)

模板如下:

1.数组模拟队列实现拓扑排序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool topsort()
{
    int hh = 0, tt = -1;
    // in[i] 存储点i的入度
    for (int i = 1; i <= n; i ++ )// 将所有入度为0的点加入队列
        if (in[i]==0)
            top[ ++ tt] = i;
    while (hh <= tt)
    {
        int t = top[hh ++ ];//找到入度为0的队头
  //遍历一下以t为头节点的的单链表,给每一个结点都要减去1,并再次找到入度为0的点
        for (int i = h[t]; i != -1; i = ne[i])
        {
        // 遍历 t 点的出边
            int j = e[i];
            if (-- in[j] == 0)//将入度减1,如果 j 入度为0,加入队列当中
                top[ ++ tt] = j;
        }
    }
    // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
    return tt == n - 1;
}

2.使用STL queue实现拓扑排序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool topsort(){
    queue<int> q;
    int t;
    for(int i = 1;i <= n; ++i)// 将所有入度为0的点加入队列
        if(in[i] == 0) q.push(i);
    while(q.size()){
        t = q.front();//每次取出队列的首部
        top[cnt] = t;//加入到 拓扑序列中
        cnt ++; // 序列中的元素 ++
        q.pop();
        for(int i = h[t];i != -1; i = ne[i]){
            // 遍历 t 点的出边
            int j = e[i];
            in[j] --;// j 的入度 --
            if(in[j] == 0) q.push(j); //如果 j 入度为0,加入队列当中
        }
    }
    if(cnt < n) return 0;
    else return 1;
}

时间复杂度 O(n+m), n表示点数,m表示边数

三、有向图的拓扑排序

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。

思路

我们每次找到入读为0的点,然后把他插入到队列里,然后将这个点删除,这也就意味着这个点连接的下一个点(可能是多个)的入度就会减1。

这个时候,我们就进入了下一轮。

我们因为前面将一个点删除了,那么它指向的点的入度就会都减去1,所以,就会出现新的点的入度为0,这个点显然是因为它的入度小,所以它理所应当的排在拓扑序里面在第二位。当前面的一个点没有了被删除了,那它就要首当其冲了。

和图的BFS思路很像,但是加了搜索的规则(即入度为零的先被搜索)可以看点这里

AC代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int e[N],ne[N],h[N],idx,in[N],n,m,top[N],cnt = 1;
// e,ne,h,idx 邻接表模板
// in 代表每个元素的入度
// top是拓扑排序的序列,cnt代表top中有多少个元素
void add(int a,int b){
    e[idx] = b; ne[idx] = h[a];h[a] = idx ++;
}
bool topsort(){
    queue<int> q;
    int t;
    for(int i = 1;i <= n; ++i)// 将所有入度为0的点加入队列
        if(in[i] == 0) q.push(i);
    while(q.size()){
        t = q.front();//每次取出队列的首部
        top[cnt] = t;//加入到 拓扑序列中
        cnt ++; // 序列中的元素 ++
        q.pop();
        for(int i = h[t];i != -1; i = ne[i]){
            // 遍历 t 点的出边
            int j = e[i];
            in[j] --;// j 的入度 --
            if(in[j] == 0) q.push(j); //如果 j 入度为0,加入队列当中
        }
    }
    if(cnt < n) return 0;
    else return 1;
}
int main(){
    int a,b;
    cin >> n >> m;
    memset(h,-1,sizeof h);//给头节点赋值为-1;
    while(m--){
        cin >> a >> b;
        add(a,b);
        in[b] ++;// a -> b , b的入度++
    }
    if(topsort() == 0) cout << "-1";
    else {
        for(int i = 1;i <= n; ++i){
            cout << top[i] <<" ";
        }
    }
    return 0;
}

到此这篇关于C++详细讲解图的拓扑排序的文章就介绍到这了,更多相关C++拓扑排序内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/m0_63233163/article/details/125001667

延伸 · 阅读

精彩推荐
  • C/C++浅析C语言位域和位段

    浅析C语言位域和位段

    以下是对C语言中的位域和位段进行了详细的分析介绍,需要的朋友可以过来参考下...

    C语言教程网4862020-12-22
  • C/C++C++中关键字 override 的简析

    C++中关键字 override 的简析

    这篇小文来聊聊 C++中的关键字 override,它的含义其实两句话就说完了,但为了叙述的完整性,让我们从虚函数说起。感兴趣的小伙伴可以跟着小编一起学习...

    干燥剂10242022-01-12
  • C/C++解析C++哈夫曼树编码和译码的实现

    解析C++哈夫曼树编码和译码的实现

    本篇文章主要介绍了C++哈夫曼树编码和译码的实现,详细的讲诉了哈夫曼树编码的原理,有需要的同学可以了解一下。...

    Dmego3702021-04-20
  • C/C++C++之BOOST字符串查找示例

    C++之BOOST字符串查找示例

    这篇文章主要介绍了C++之BOOST字符串查找的方法,实例演示了boost针对字符串的查找、判定及替换等操作,具有一定的实用价值,需要的朋友可以参考下...

    C++教程网10402021-02-18
  • C/C++C语言编程C++柔性数组结构示例讲解

    C语言编程C++柔性数组结构示例讲解

    这篇文章主要介绍了C语言编程系列中的柔性数组,文中含有详细的示例代码讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助...

    小码农UU5092022-01-17
  • C/C++如何在C语言中判断socket是否已经断开

    如何在C语言中判断socket是否已经断开

    如果不主动关闭socket的话,系统不会自动关闭的,除非当前进程挂掉了,操作系统把占用的socket回收了才会关闭。小编今天跟大家简单介绍下如何在C语言中判断...

    Anyanyamy7912021-07-29
  • C/C++详谈C++ socket网络编程实例(2)

    详谈C++ socket网络编程实例(2)

    这篇文章主要为大家介绍了C++ socket网络编程实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助...

    ufgnix080212052022-02-28
  • C/C++C语言简明讲解队列的实现方法

    C语言简明讲解队列的实现方法

    队列(Queue)与栈一样,是一种线性存储结构,它具有如下特点:队列中的数据元素遵循“先进先出”(First In First Out)的原则,简称FIFO结构。在队尾添加...

    平凡的人17582022-11-14