圖應用之拓撲排序(Topological Sort)

這一篇寫有向無環圖及其它的應用:數組

 

清楚概念:數據結構

有向無環圖(DAG):一個無環的有向圖。通俗的講就是從一個點沿着有向邊出發,不管怎麼遍歷都不會回到出發點上。工具

有向無環圖是描述一項工程或者系統的進行過程的有效工具,好比辦公室,到工商局裏面註冊的時候,他會提示你一個流程,這個流程就是一個有向無環圖。測試

第一步不作,第二步就作不了。spa

在其期間關心兩個問題:3d

1.工程是否順利?(拓撲排序)指針

2.估算整個工程所必須的最短期。(關鍵路徑)blog

 

拓撲排序:排序

數學語言:某個集合上的一個偏序獲得該集合上的一個全序的操做過程。隊列

百度百科:

拓撲序列

一般,這樣的線性序列稱爲知足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序獲得該集合上的一個全序,這個操做稱之爲拓撲排序。離散數學中關於偏序和全序的定義:

若集合X上的關係是R,且R是自反的、反對稱的和傳遞的,則稱R是集合X上的偏序關係。

設R是集合X上的偏序(Partial Order),若是對每一個x,y屬於X必有xRy 或 yRx,則稱R是集合X上的全序關係。

比較簡單的理解:偏序是指集合中只有部分紅員能夠比較,全序是指集合中全部的成員之間都可以比較。

注意:

①若將圖中頂點按拓撲次序排成一行,則圖中全部的有向邊均是從左指向右的。

②若圖中存在有向環,則不可能使頂點知足拓撲次序。

③一個DAG的拓撲序列一般表示某種方案切實可行。

看下面一幅圖:

這是一個偏序,1到5,能夠 1-3-5 也能夠是 1-2-5

2和3沒有前後,咱們認爲的加上 2先於3 或者 3先於2,這樣的過程就將該偏序圖(1,2,3,5)變成了全序圖。

咱們定義1-2-3-5這個序列稱爲拓撲有序。而這個輸出的過程就是拓撲排序。拓撲排序結果不止一種。這個圖能夠看到

1-2-3-5 和1-3-2-5都是排序的結果。

拓撲排序:就是對一個有向圖構造拓撲有序的過程。

 

咱們把每一個頂點看做是一個子過程,邊表示子過程的優先順序,這樣的圖咱們能夠定義爲AOV。AOV(Activity On Vertex Network)

 

若是拓撲排序:

(1)在有向圖中選一個沒有前驅的頂點且輸出之

(2)從圖中刪除該頂點和全部以它爲尾的弧

重複上述步驟,直至所有點輸出,或者當圖中的頂點不存在前驅爲止(有環)。

 

涉及到了有向圖,有前驅和後驅,這裏的數據結構也要跟着改變。

以前使用過鄰接表,這裏須要在鄰接表的頂點信息裏面添加一個入度的信息便可。

#define MAXVEX 100
#define IFY 65535


typedef char VertexType;
typedef int  EdgeType;
typedef int  IdxType;
typedef int QueueType;
typedef int StackType;

///---------------------------------------
//邊節點
typedef struct EdgeNode{
    IdxType idx;
    struct EdgeNode* next;
}eNode;

//頂點節點
typedef struct VexNode{
    int numIn;        //入度數量
    IdxType idx;
    eNode *fitstedge;
}vNode;

//圖的集合:包含了一個頂點數組
typedef struct {
    vNode adjList[MAXVEX];
    int numVextexs,numEdges;
}GraphAdjList;

拓撲排序的代碼:

int TopplogicalSort(GraphAdjList *g)
{
    int count=0;
    eNode *e=NULL;
    StackType *stack=NULL;
    StackType top=0;
    stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
    int i;

    for (i=0;i<(*g).numVextexs;i++)
    {
        if (!(*g).adjList[i].numIn)
        {
            stack[++top] = i;
    //        printf("init no In is %c\n",g_init_vexs[i]);
        }
    }
    

    while(top)
    {
        int geter = stack[top];
        top--;

        printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
        count++;

        //獲取當前點出度的點,對出度的點的入度減一(當前點要出圖)。
        //獲取當前頂點的出度點表
        e = (*g).adjList[geter].fitstedge;
        while(e)
        {
            //選取的出度點的入度減一
            int crntIN = --(*g).adjList[e->idx].numIn;
            if (crntIN == 0)
            {
                //若是爲0,則說明該頂點沒有入度了,是下一輪的輸出點。
                stack[++top] = e->idx;
        //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
            }
            e = e->next;
        }
    }
    if (count < (*g).numVextexs)//若是圖自己就是一個大環,或者圖中含有環,這樣有環的頂點不會進棧而被打印出來。
    {
        return false;
    }
    else
    {
        printf("finish\n");
        return true;
    }
    
}

如下圖爲例作測試:

找出入度爲0的頂點:A、G 

 

 

 

源代碼:

// grp-top-sort.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <stdlib.h>


#define MAXVEX 100
#define IFY 65535


typedef char VertexType;
typedef int  EdgeType;
typedef int  IdxType;
typedef int QueueType;
typedef int StackType;

///---------------------------------------
//邊節點
typedef struct EdgeNode{
    IdxType idx;
    struct EdgeNode* next;
}eNode;

//頂點節點
typedef struct VexNode{
    int numIn;        //入度數量
    IdxType idx;
    eNode *fitstedge;
}vNode;

//圖的集合:包含了一個頂點數組
typedef struct {
    vNode adjList[MAXVEX];
    int numVextexs,numEdges;
}GraphAdjList;

///-----------------------------------
VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I','J','K','L'};

char *g_input[] = {
    "A->B->C->D",
    "B->E",
    "C->F->I->J",
    "D->E->I->J",
    "E",
    "F->K",
    "G->F->H->K",
    "H->I",
    "I->J->L",
    "J->E->K",
    "K->L",
    "L"
};

//===============================================================
//隊列

//隊列節點
typedef struct Node {
    QueueType data;
    struct Node *next;
}QNode,*qQNode;

//隊列指示
typedef struct {
    int length;
    qQNode frnt,rear;    
}spQueue;

void init_Queue(spQueue *Q)
{
    (*Q).frnt = NULL;
    (*Q).rear = NULL;
    (*Q).length = 0;
}
bool isEmptyQueue(spQueue Q)
{
    if (Q.length == 0)
    {
        return true;
    }
    return false;
}
//進隊
void unshiftQueue(spQueue *Q,QueueType elem)
{
    //隊列空
    if (isEmptyQueue(*Q))
    {
        qQNode n = (qQNode)malloc(sizeof(QNode));
        n->data = elem;
        n->next = NULL;

        (*Q).frnt = n;
        (*Q).rear = n;
        (*Q).length = 1;
    }
    else
    {
        qQNode n = (qQNode)malloc(sizeof(QNode));
        n->data = elem;
        n->next = NULL;

        (*Q).rear->next = n;

        (*Q).rear = n;
        (*Q).length++;
    }
}

//出隊
QueueType shiftQueue(spQueue *Q)
{
    if (isEmptyQueue(*Q))
    {
        printf("Warning:Queue is empty!!!\n");
        return NULL;
    }
    if ((*Q).length == 1)
    {
        QueueType sh = (*Q).frnt->data;
        (*Q).frnt = NULL;
        (*Q).rear = NULL;
        (*Q).length = 0;
        return sh;
    }
    QueueType sh = (*Q).frnt->data;
    (*Q).frnt = (*Q).frnt->next;
    (*Q).length--;

    return sh;
}

//打印隊列
void prt_que(spQueue que)
{
    if (isEmptyQueue(que))
    {
        return ;
    }
    qQNode pos = que.frnt;
    while(que.rear->next != pos && pos != NULL)
    {
        printf(" %d ",pos->data);
        pos = pos->next;
    }
    printf("\n");
}
//===============================================================

///-------
//由節點找節點的序號
IdxType strFindIdx(char ch)
{
    int i=0;
    VertexType *p = g_init_vexs;
    while(p != NULL)
    {
        if(*p == ch)
        {
            return i;
        }
        p++;
        i++;
    }
    return i;
}

//由序號找節點
VertexType idxFindStr(IdxType i)
{
    return g_init_vexs[i];
}

void prt_strings(char *p)
{
    char *pos = p;
    while (NULL != *pos)
    {
        printf("%c",*pos);
        pos++;
    }
    printf("\n");
}

void prt_strArrays(char *p[])
{
    char **pos = p; 
    while( *pos != NULL)
    {
        prt_strings(*pos);
        pos++;
    }
}

//我規定:頂點只能是大寫。
bool isVexter(char p)
{
    if (p>='A' && p<='Z')
    {
        return true;
    }
    return false;
}



void init_GrapAdjList(GraphAdjList *g,char **str)
{
    char **pos = str;
    int cnt=0;

    //入度清零
    int i;
    for (i=0;i<MAXVEX;i++)
    {
        (*g).adjList[i].numIn = 0;
    }

    while (*pos != NULL) //g_input的每行的首指針
    {
        int i=0;
        while(**pos != NULL) //g_input的每行字母
        {
            if(isVexter(**pos)) //判斷是否爲頂點(我規定‘A’-‘Z’之間爲頂點標誌)
            {
                if (i == 0) //創建頂點的節點
                {
                    (*g).adjList[cnt].idx = strFindIdx(**pos);
                    (*g).adjList[cnt].fitstedge = NULL;
                    
                    i=1;
                }
                else if(i == 1) //創建第一個邊的節點
                {
                    eNode* n = (eNode*)malloc(sizeof(eNode));
                    n->idx = strFindIdx(**pos);
                    n->next = NULL;

                    (*g).adjList[cnt].fitstedge = n;
                    i=2;
                    
                    //添加入度
                    int iidx = strFindIdx(**pos);
                    (*g).adjList[iidx].numIn++;
                }
                else //邊節點鏈接到前一個邊節點上
                {    
                    eNode* n = (eNode*)malloc(sizeof(eNode));
                    n->idx = strFindIdx(**pos);
                    n->next = NULL;

                    //first splist
                    eNode *r = (*g).adjList[cnt].fitstedge;
                    while (r->next != NULL)
                    {
                        r = r->next;
                    }
                    r->next = n;

                    //添加入度
                    int iidx = strFindIdx(**pos);
                    (*g).adjList[iidx].numIn++;
                }
            }
            (*pos)++; 
        }
        

        pos++;
        cnt++;
    }
    (*g).numVextexs = cnt;
}

int TopplogicalSort(GraphAdjList *g)
{
    int count=0;
    eNode *e=NULL;
    StackType *stack=NULL;
    StackType top=0;
    stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
    int i;

    for (i=0;i<(*g).numVextexs;i++)
    {
        if (!(*g).adjList[i].numIn)
        {
            stack[++top] = i;
    //        printf("init no In is %c\n",g_init_vexs[i]);
        }
    }
    

    while(top)
    {
        int geter = stack[top];
        top--;

        printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
        count++;

        //獲取當前點出度的點,對出度的點的入度減一(當前點要出圖)。
        //獲取當前頂點的出度點表
        e = (*g).adjList[geter].fitstedge;
        while(e)
        {
            //選取的出度點的入度減一
            int crntIN = --(*g).adjList[e->idx].numIn;
            if (crntIN == 0)
            {
                //若是爲0,則說明該頂點沒有入度了,是下一輪的輸出點。
                stack[++top] = e->idx;
        //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
            }
            e = e->next;
        }
    }
    if (count < (*g).numVextexs)//若是圖自己就是一個大環,或者圖中含有環,這樣有環的頂點不會進棧而被打印出來。
    {
        return false;
    }
    else
    {
        printf("finish\n");
        return true;
    }
    
}

int _tmain(int argc, _TCHAR* argv[])
{
    GraphAdjList grp;
    prt_strArrays(g_input);
    init_GrapAdjList(&grp,g_input);
    if (!TopplogicalSort(&grp))
    {
        printf("grp wrong!\n");
    }
    
    getchar();
    return 0;
}

運行結果:

相關文章
相關標籤/搜索