图的拓扑排序

Posted geekfx

tags:

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

拓扑排序

拓扑排序是对有向无圈图的顶点的一种排序,它使得如果存在一条从(v_i)(v_j)的路径,那么在排序中(v_j)出现在(v_i)的后面。如果图含有圈,那么拓扑排序是不可能的。此外,排序不必是唯一的;任何合理的排序都是可以的。

算法思想

第1种

先找出任意一个没有入边的顶点。然后显示出该顶点,并将它和它的边一起从图中删除。然后对图的其余部分应用同样的方法处理。

伪代码

void Topsort(Graph G)
{
    int Counter;
    Vertex V, W;

    for(Counter = 0; Counter < NumVertex; Counter++)
    {
        V = FindNewVertexOfIndegreeZero();
        if(V == NotAVertex)
        {
            Error("Graph has a cycle");
            break;
        }
        TopNum[V] = Counter;    // 或打印出
        for each W adjancent to V
            Indegree[W]--;
    }
}

函数FindNewVertexOfIndegreeZero扫描Indegree数组,寻找一个尚未被分配拓扑编号的入度为0的顶点;因为其是对Indegree数组的一个简单的顺序扫描,所以每次对它的调用都花费(O(left | V ight |))的时间.由于有(left | V ight |)这样的调用,因此该算法的运行时间为(O({left | V ight |}^2))

第2种

首先,对每一个顶点计算它的入度(可以在输入图时就计算)。然后将所以入度为0的顶点放入一个初始为空的的队列中。当队列不空时,删除一个顶点(v),并将(v)邻接的所有的顶点的入度减1.只要1个顶点的入度将为0,就把该顶点放入队列中。此时,拓扑排序就是顶点出队的顺序。

C语言代码

void Topsort(ALGraph G)
{
    Queue Q;
    int Counter;
    VertexType V;
    ArcNode W;

    Q = CreateQueue(MVNum);
    Counter = 0;

    for (V = 1; V <= G->vexnum; V++)
        if (G->Indegree[V] == 0)
            Enqueue(V, Q);

    while (!IsEmpty(Q))
    {
        V = FrontAndDequeue(Q);
        printf("%d->", V);
        Counter++;

        W = G->vertices[V].firstarc;
        while (W != NULL)
        {
            if (--G->Indegree[W->adjvex] == 0)
                Enqueue(W->adjvex, Q);
            W = W->nextarc;
        }
    }
    printf("^
");

    if (Counter != G->vexnum)
        printf("Graph has a cycle
");

    DisposeQueue(Q);
}

如果使用邻接表,那么执行这个算法所用的时间为(O(left | E ight | +left | V ight | ))

完整C语言代码

#include <stdio.h>
#include <stdlib.h>

#define NotAVertex (-1)
#define MVNum (10) // 最大顶点数
typedef int VertexType;
typedef int ArcType;

typedef struct ArcNode // 边结点
{
    VertexType adjvex;       // 该边所指向的顶点的位置
    struct ArcNode *nextarc; // 指向下一条边的指针
    ArcType weight;          // 边的权值
} * ArcNode;

typedef struct VNode // 顶点信息
{
    // VertexType data;
    ArcNode firstarc;    // 指向第一条依附该顶点的边的指针
} VNode, AdjList[MVNum]; // AdjList 表示邻接类型

typedef struct // 邻接表
{
    AdjList vertices;
    int vexnum, arcnum;  // 图的当前定点数和边数
    int Indegree[MVNum]; // 每个顶点的入度
} * ALGraph;

void CreateUDG(ALGraph G)
{
    int i, j;
    int v1, v2;
    ArcNode p1, p2;
    ArcType weight;

    printf("Input vexnum: ");
    scanf("%d", &G->vexnum);
    printf("Input arcnum: ");
    scanf("%d", &G->arcnum);

    // 输入各点,构造表头结点表
    for (i = 1; i <= G->vexnum; i++)
    {
        G->vertices[i].firstarc = NULL;
        G->Indegree[i] = 0;
    }

    // 输入一条边依附的两个顶点
    for (j = 0; j < G->arcnum; j++)
    {
        scanf("%d%d%d", &v1, &v2, &weight);
        p1 = (ArcNode)malloc(sizeof(*p1));
        if (p1 == NULL)
            exit(0);
        p1->adjvex = v2;
        p1->weight = weight;
        p1->nextarc = G->vertices[v1].firstarc;
        G->vertices[v1].firstarc = p1;
        G->Indegree[v2] += 1;
        /*p2 = (ArcNode)malloc(sizeof(struct ArcNode));
        p2->adjvex = i;
        p2->nextarc = G->vertices[j].firstarc;
        G->vertices[j].firstarc = p2;
        G->Indegree[v1] += 1;*/
    }
}

ALGraph CreateGraph(void)
{
    ALGraph G;
    G = (ALGraph)malloc(sizeof(*G));
    if (G == NULL)
        exit(0);
    return G;
}

typedef VertexType ElementType;
typedef struct QueueNode *Queue;
struct QueueNode
{
    ElementType Array[MVNum];
    int Capacity;
    int Size;
    int Front;
    int Rear;
};
int IsFull(Queue Q)
{
    return Q->Size == Q->Capacity;
}
int IsEmpty(Queue Q)
{
    return Q->Size == 0;
}

Queue CreateQueue(int Capacity)
{
    Queue Q;
    Q = (Queue)malloc(sizeof(struct QueueNode));
    if (Q == NULL)
        exit(0);
    Q->Capacity = Capacity;
    Q->Size = 0;
    Q->Front = 1;
    Q->Rear = 0;
    return Q;
}

void Enqueue(ElementType X, Queue Q)
{
    if (IsFull(Q))
    {
        printf("The queue is full
");
        system("pause");
        exit(0);
    }
    Q->Size++;
    if (++Q->Rear == Q->Capacity)
        Q->Rear = 0;
    Q->Array[Q->Rear] = X;
}

void Dequeue(Queue Q)
{
    if (IsEmpty(Q))
    {
        printf("The queue is empty
");
        system("pause");
        exit(0);
    }
    Q->Size--;
    if (++Q->Front == Q->Capacity)
        Q->Front = 0;
}

ElementType Front(Queue Q)
{
    if (IsEmpty(Q))
    {
        printf("The queue is empty
");
        system("pause");
        exit(0);
    }
    return Q->Array[Q->Front];
}

ElementType FrontAndDequeue(Queue Q)
{
    if (IsEmpty(Q))
    {
        printf("The queue is empty
");
        system("pause");
        exit(0);
    }
    Q->Size--;
    return Q->Array[(Q->Front++) % Q->Capacity];
}

void DisposeQueue(Queue Q)
{
    free(Q);
}

void Topsort(ALGraph G)
{
    Queue Q;
    int Counter;
    VertexType V;
    ArcNode W;

    Q = CreateQueue(MVNum);
    Counter = 0;

    for (V = 1; V <= G->vexnum; V++)
        if (G->Indegree[V] == 0)
            Enqueue(V, Q);

    while (!IsEmpty(Q))
    {
        V = FrontAndDequeue(Q);
        printf("%d->", V);
        Counter++;

        W = G->vertices[V].firstarc;
        while (W != NULL)
        {
            if (--G->Indegree[W->adjvex] == 0)
                Enqueue(W->adjvex, Q);
            W = W->nextarc;
        }
    }
    printf("^
");

    if (Counter != G->vexnum)
        printf("Graph has a cycle
");

    DisposeQueue(Q);
}

int main()
{
    ALGraph G;
    G = CreateGraph();
    CreateUDG(G);
    Topsort(G);
    system("pause");
    return 0;
}

输入/输出

顶点按数字编号,第3个数子为边上的权值。

Input vexnum: 7
Input arcnum: 12
1 2 1
1 3 1
3 6 1
7 6 1
5 7 1
2 5 1
1 4 1
2 4 1
5 4 1
4 7 1
4 6 1
4 3 1
1->2->5->4->3->7->6->^
请按任意键继续. . .

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

图的拓扑排序是否不唯一的?

图的应用——拓扑排序(判断有向图有无回路)

图的遍历:拓扑排序

一个有向无环图的拓扑排序序列是否唯一的

图的拓扑排序

图的拓扑排序