拓扑排序

拓扑排序

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拿个例子来说,比如下面一张图:

在这里插入图片描述
上面的图中的1的顶点的入度为0,先去掉1顶点,去掉1顶点进入其他顶点的边(去掉1->2,去掉1->3),这时2和3d的顶点的入度
变为0,去掉2顶点,去掉(2->4)边,去掉3顶点,去掉(3->4)边,注意到4这个顶点的时候就出现了环,而拓扑排序计算的是有向无环图,有环的话是计算不出拓扑排序的。就好像是4代表C语言课程,4是离散数学,6是数据结构,咱们先认定先学C语言课程和离散数学才能学数据结构,像下面的图那样。
在这里插入图片描述
如果反过的话就违背之前的规定,打乱了顺序而导致不知道先学那一科。
如果改成上面的图的话,就可以计算出拓扑排序为1 2 3 4 5 6。

这个时候你差不多知道拓扑排序的原理是什么了,但如何去计算拓扑排序呢?好,下面我再举个例子为大家讲解一下。

在这里插入图片描述
首先我们要创建邻接表:

在这里插入图片描述

接着我们要定义一个一维数组作为栈来使用,上面有6个顶点,我们可以a[6],数据的元素填每个顶点的入度情况。
先用数组的下标代替顶点。
在这里插入图片描述

利用数组栈遍历顶点

在这里插入图片描述
第一步 : 4 , 1 入栈
第二步: 4 , 1 出栈
第三步 :2 入栈
第四步: 2 出栈
第五步 : 3, 5 入栈
第六步: 3 , 5 出栈

拓扑排序:

//使用数组栈实现拓扑排序
void TopoSort(ALGraph* G)
{
    int i, getTop,j, k, top, count = 0;
    EdgeNode* p;
    int stack[20]; //入度个数
    int* list_in = (int*)malloc((G->n)*sizeof(int));   //数组栈
    for (i = 1;i <= G->n;i++)		//初始化数组
    {
        list_in[i] = 0;
    }
    for (i = 1;i <= G->n;i++)		//统计各个顶点的入度情况,并把他们填入数组里面
    {
        p = G->adjlist[i].firstedge;
        while (p != NULL)
        {
            j = p->adjvex;   //某个顶点的出度也就相当于该顶点的出度
            list_in[j]++;   //该顶点的入度加1
            p = p->next;
        }
    }
    top = 0;
    for (i = 1;i <= G->n; i++)			//先找出里面入度是0的顶点
    {
        if (list_in[i] == 0)
        {
            stack[++top] = i;  //寻找入度为0的顶点,在进栈
        } 
    }

    while (top != 0)
    {
        getTop = stack[top--];   //取出栈顶元素,出栈
        printf("%d ", G->adjlist[getTop].vertex);
        count++;		//统计顶点
        p = G->adjlist[getTop].firstedge;
        while (p)
        {
            k = p->adjvex;		//相l连接的顶点
            list_in[k]--;		//相连接的顶点入度减1
            if (list_in[k] == 0)		//如果发现入度为0的新顶点,该顶点进栈
            {
                stack[++top] = k;
            }
            p = p->next;
        }

    }
    if (count < G->n) printf("\n有回路!\n");
}

邻接表建立


typedef int Boolean;
typedef int VertexType;
Boolean visit[MaxVertexNum];
typedef struct node
{
    int adjvex;    //顶点下标
    struct node* next;    //指向下一一个节点
}EdgeNode;

typedef struct
{
    VertexType vertex;  //顶点数据
    EdgeNode* firstedge;  //指向第一条边
}VertexNode;

typedef VertexNode AdjList[MaxVertexNum];

typedef struct
{
    AdjList adjlist;    //顶点数组
    int n, e;   //顶点数  , 边数
}ALGraph;

//查找顶点下标--->位置
int FindVertex(ALGraph* G, int e)
{
    int i;

    for (i = 1;i <= G->n ;i++)
    {
        if (G->adjlist[i].vertex == e)
        {
            return i;
        }
    }
    return -1;
}
void create(ALGraph* G)			//创建邻接表
{
    int i, j, k;
    EdgeNode* s;

    printf("读入顶点和边数");
    scanf_s("%d %d", &G->n, &G->e);

    for (i = 1;i <= G->n;i++)
    {

        printf("建立顶点表");
        fflush(stdin);
        scanf_s("%d", &G->adjlist[i].vertex);
        G->adjlist[i].firstedge = NULL;
    }
    printf("建立边表\n");
    for (k = 1;k <= G->e;k++)
    {
        printf("读入(vi-vj)的顶点对序号");
        scanf_s("%d %d", &i, &j);

        i = FindVertex(G, i);
        j = FindVertex(G, j);
        while (i == -1 || j == -1)
        {
            printf("找不到顶点,请重新输入!\n");
            printf("读入(vi-vj)的顶点对序号");
            scanf_s("%d %d", &i, &j);
            i = FindVertex(G, i);
            j = FindVertex(G, j);
        }
        s = (EdgeNode*)malloc(sizeof(EdgeNode));
        s->adjvex = j;
       //头插 
        s->next = G->adjlist[i].firstedge;
        G->adjlist[i].firstedge = s;
    }
}

//打印邻接表
void PrintALG(ALGraph* G)
{
    EdgeNode* p;
    int i;
    printf("其邻接表为('->'表示两个之间有连接):\n");
    for (i = 1;i <= G->n;i++)
    {
        p = G->adjlist[i].firstedge;
        printf("%d", G->adjlist[i].vertex);
        if (G->adjlist[i].firstedge != NULL)
        {   //若该顶点没有出度则不打印连接符
            printf("->");
        }
        while (p != NULL)
        {
            printf("%d", G->adjlist[p->adjvex].vertex);
            if (p->next != NULL)   //取消最后一个节点的连接符
            {
                printf("->");
            }
            p = p->next;
        }
        printf("\n");
    }

}

完整代码

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

#define MaxVertexNum 100
#define ERROR 0
#define OK 1
#define FALSE 0
#define TRUE 1

typedef int Boolean;
typedef int VertexType;
Boolean visit[MaxVertexNum];
typedef struct node
{
    int adjvex;    //顶点下标
    struct node* next;    //指向下一一个节点
}EdgeNode;

typedef struct
{
    VertexType vertex;  //顶点数据
    EdgeNode* firstedge;  //指向第一条边
}VertexNode;

typedef VertexNode AdjList[MaxVertexNum];

typedef struct
{
    AdjList adjlist;    //顶点数组
    int n, e;   //顶点数  , 边数
}ALGraph;

//查找顶点下标--->位置
int FindVertex(ALGraph* G, int e)
{
    int i;

    for (i = 1;i <= G->n ;i++)
    {
        if (G->adjlist[i].vertex == e)
        {
            return i;
        }
    }
    return -1;
}
void create(ALGraph* G)			//创建邻接表
{
    int i, j, k;
    EdgeNode* s;

    printf("读入顶点和边数");
    scanf_s("%d %d", &G->n, &G->e);

    for (i = 1;i <= G->n;i++)
    {

        printf("建立顶点表");
        fflush(stdin);
        scanf_s("%d", &G->adjlist[i].vertex);
        G->adjlist[i].firstedge = NULL;
    }
    printf("建立边表\n");
    for (k = 1;k <= G->e;k++)
    {
        printf("读入(vi-vj)的顶点对序号");
        scanf_s("%d %d", &i, &j);

        i = FindVertex(G, i);
        j = FindVertex(G, j);
        while (i == -1 || j == -1)
        {
            printf("找不到顶点,请重新输入!\n");
            printf("读入(vi-vj)的顶点对序号");
            scanf_s("%d %d", &i, &j);
            i = FindVertex(G, i);
            j = FindVertex(G, j);
        }
        s = (EdgeNode*)malloc(sizeof(EdgeNode));
        s->adjvex = j;
       //头插 
        s->next = G->adjlist[i].firstedge;
        G->adjlist[i].firstedge = s;
    }
}

//打印邻接表
void PrintALG(ALGraph* G)
{
    EdgeNode* p;
    int i;
    printf("其邻接表为('->'表示两个之间有连接):\n");
    for (i = 1;i <= G->n;i++)
    {
        p = G->adjlist[i].firstedge;
        printf("%d", G->adjlist[i].vertex);
        if (G->adjlist[i].firstedge != NULL)
        {   //若该顶点没有出度则不打印连接符
            printf("->");
        }
        while (p != NULL)
        {
            printf("%d", G->adjlist[p->adjvex].vertex);
            if (p->next != NULL)   //取消最后一个节点的连接符
            {
                printf("->");
            }
            p = p->next;
        }
        printf("\n");
    }

}

//使用数组栈实现拓扑排序
void TopoSort(ALGraph* G)
{
    int i, getTop,j, k, top, count = 0;
    EdgeNode* p;
    int stack[20]; //入度个数
    int* list_in = (int*)malloc((G->n)*sizeof(int));   //数组栈
    for (i = 1;i <= G->n;i++)		//初始化数组
    {
        list_in[i] = 0;
    }
    for (i = 1;i <= G->n;i++)		//统计各个顶点的入度情况,并把他们填入数组里面
    {
        p = G->adjlist[i].firstedge;
        while (p != NULL)
        {
            j = p->adjvex;   //某个顶点的出度也就相当于该顶点的出度
            list_in[j]++;   //该顶点的入度加1
            p = p->next;
        }
    }
    top = 0;
    for (i = 1;i <= G->n; i++)			//先找出里面入度是0的顶点
    {
        if (list_in[i] == 0)
        {
            stack[++top] = i;  //寻找入度为0的顶点,在进栈
        } 
    }

    while (top != 0)
    {
        getTop = stack[top--];   //取出栈顶元素,出栈
        printf("%d ", G->adjlist[getTop].vertex);
        count++;		//统计顶点
        p = G->adjlist[getTop].firstedge;
        while (p)
        {
            k = p->adjvex;		//相l连接的顶点
            list_in[k]--;		//相连接的顶点入度减1
            if (list_in[k] == 0)		//如果发现入度为0的新顶点,该顶点进栈
            {
                stack[++top] = k;
            }
            p = p->next;
        }

    }
    if (count < G->n) printf("\n有回路!\n");
}

int main()
{
    ALGraph* G = (ALGraph*)malloc(sizeof(ALGraph));
    create(G);
    PrintALG(G);
    printf("拓扑排序为:\n");
    TopoSort(G);
    getchar();
    return 0;
}

在这里插入图片描述
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章