拓撲排序

拓撲排序

對一個有向無環圖(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;
}

在這裏插入圖片描述
在這裏插入圖片描述

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