图的拓扑排序——卡恩算法

Posted amaris-diana

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图的拓扑排序——卡恩算法相关的知识,希望对你有一定的参考价值。

拓扑排序

有向图的拓扑排序是其顶点的线性排序,使得对于从顶点u 到顶点v 的每个有向边uv ,u 在排序中都在v 之前。 

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序Topological sorting)。

  1. 每个顶点出现且只出现一次;
  2. 若A在序列中排在B的前面,则在图中不存在从B到A的路径
技术图片
//#include<Windows.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 105;
const int MAX_E = 10005;
const int MAX_V = 105;

struct ENode
{
    int to;
    int Next;
};
ENode Edegs[MAX_E];
int Head[MAX_V];
int tnt;
void Add_ENode(int w, int v)
{
    ++tnt;
    Edegs[tnt].to = v;
    Edegs[tnt].Next = Head[w];
    Head[w] = tnt;
    /*可以拓扑排序则保证这是一个有向图*/
}
int IN_degree[maxn];//记录每个点的入度;
int Queue[maxn];
int main()
{
    int n, m;
    int u, v;
    scanf_s("%d %d", &n, &m);
    memset(IN_degree, 0, sizeof(IN_degree));
    memset(Head, -1, sizeof(Head));
    tnt = -1;
    for (int i = 0; i < m; i++)
    {
        scanf_s("%d %d", &u, &v);
        Add_ENode(u, v);
        IN_degree[v] ++;
    }

    int iq = 0;
    for (int i = 1; i <= n; i++)
    {
        if (IN_degree[i] == 0)
        {
            Queue[iq++] = i;
        }
    }
    for (int i = 0; i < iq; i++)
    {
        for (int k = Head[Queue[i]]; k != -1; k = Edegs[k].Next)
        {
            IN_degree[Edegs[k].to] --;
            if (IN_degree[Edegs[k].to] == 0)
            {
                Queue[iq++] = Edegs[k].to;
            }
        }
    }
    for (int i = 0; i <= iq; i++)
    {
        printf("%d%c", Queue[i], i != iq - 1 ?   : 
);
    }
//    system("pause");
    return 0;
}
拓扑排序,卡恩算法

 

卡恩算法

  假设L 是存放结果的列表,先找到那些入度为零的节点,把这些节点放到L 中,因为这些节点没有任何的父节点。然后把与这些节点相连的边从图中去掉,再寻找图中的入度为零的节点。对于新找到的这些入度为零的节点来说,他们的父节点已经都在L中了,所以也可以放入L。重复上述操作,直到找不到入度为零的节点。如果此时L中的元素个数和节点总数相同,说明排序完成;如果L中的元素个数和节点总数不同,说明原图中存在环,无法进行拓扑排序。

 

概括起来,即以下三步:

  ①从有向图中选择一个没有前驱(入度为0)的点,输出它。

  ②从网(图)中删除它,并且删除从它出发的所有有向边。

  ③重复上面步骤,直到图(网)中不再存在没有前驱(入度为0)的点为止。

对于上面的算法,如果最终存在不能删除的点,则剩余的点之间一定构成环路。

 

实现方式:用链式前向星存储整张图,在读入数据时统计每个点的入度。每删除一个点,就删除所有从它出发的边,并且把对应的终点入度-1,再去删除下一个点。

实现代码

技术图片
//#include<Windows.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 105;
const int MAX_E = 10005;
const int MAX_V = 105;

struct ENode
{
    int to;
    int Next;
};
ENode Edegs[MAX_E];
int Head[MAX_V];
int tnt;
void Add_ENode(int w, int v)
{
    ++tnt;
    Edegs[tnt].to = v;
    Edegs[tnt].Next = Head[w];
    Head[w] = tnt;
    /*可以拓扑排序则保证这是一个有向图*/
}
int IN_degree[maxn];//记录每个点的入度;
int Queue[maxn];
int main()
{
    int n, m;
    int u, v;
    scanf_s("%d %d", &n, &m);
    memset(IN_degree, 0, sizeof(IN_degree));
    memset(Head, -1, sizeof(Head));
    tnt = -1;
    for (int i = 0; i < m; i++)
    {
        scanf_s("%d %d", &u, &v);
        Add_ENode(u, v);
        IN_degree[v] ++;
    }

    int iq = 0;
    for (int i = 1; i <= n; i++)
    {
        if (IN_degree[i] == 0)
        {
            Queue[iq++] = i;
        }
    }
    for (int i = 0; i < iq; i++)
    {
        for (int k = Head[Queue[i]]; k != -1; k = Edegs[k].Next)
        {
            IN_degree[Edegs[k].to] --;
            if (IN_degree[Edegs[k].to] == 0)
            {
                Queue[iq++] = Edegs[k].to;
            }
        }
    }
    for (int i = 0; i <= iq; i++)
    {
        printf("%d%c", Queue[i], i != iq - 1 ?   : 
);
    }
//    system("pause");
    return 0;
}
拓扑排序

 

以上是关于图的拓扑排序——卡恩算法的主要内容,如果未能解决你的问题,请参考以下文章

拓扑排序算法实现

通用的深度优先搜索+图的应用1:拓扑排序

图的应用——拓扑排序算法

图的应用(最小生成树,拓扑排序)

图的应用(最小生成树,拓扑排序)

图的应用(最小生成树,拓扑排序)