數(shù)算---拓?fù)渑判?/h1>

概念

設(shè)G=(V,E) 是一個具有n個頂點的有向圖,V中的頂點序號V,V2,....,Vn, 若滿足從頂點Vi到Vj有一條路徑,如果頂點序列Vi 必須在Vj之前,則我們稱這樣的頂點序列為拓?fù)湫蛄?/code>,即頂點間有個先后排序問題。
如下的OC和Swift代碼編譯的過程,存在由編譯器前端(Clang\Swift)和編譯器后端(LLVM)兩部分組成,存在先后順序。


所謂的拓?fù)渑判?/code>,其實就是對一個有向圖構(gòu)造拓?fù)湫蛄械倪^程。構(gòu)造的結(jié)果有兩種情況:

  1. 如果此網(wǎng)中的全部頂點被輸出,則說明它是不存在環(huán)(回路)的AOV網(wǎng)
  2. 如果輸出的頂點數(shù)少了,說明這個網(wǎng)存在環(huán)(回路),不是AOV網(wǎng)。

思路

如下圖中的V0~V13共14個頂點,為有向圖,下面就為此圖進行拓?fù)渑判颍?/p>

有向圖的數(shù)據(jù)結(jié)構(gòu)表示用鄰接表的方式更適合拓?fù)涞挠嬎氵^程。根據(jù)上圖中頂點間的指向,得到下面的鄰接表,并用數(shù)組將每個頂點的指向情況保存起來,從V0到V3的順序保存:


基本思路:從AOV網(wǎng)中選擇一個入度為0的頂點輸出,然后刪除此頂點,并刪除指向此頂點的狐,直到輸出全部頂點或AOV網(wǎng)中不存在入度為0的頂點為止。
在這個過程中,我們還需要借助一個棧來幫助我們存放入度為0的頂點。

  1. 創(chuàng)建一個棧,用來存儲入度in為0的頂點序號(0~13);
  2. 遍歷AOV圖中頂點表,判斷入度為0的頂點全部入棧。

過程

  1. 初始化棧,將V0,V1,V3 入棧,遍歷棧,輸出棧頂(gettop = 3,并且count = 1),3為V3的序號,即數(shù)組中的下標(biāo)3。循環(huán)遍歷V3對應(yīng)的弧鏈表,找到與V3頂點連接的2個頂點V13和V2,并將其入度減1,則V13和V2的入度in為1。出棧V3的序號3,打印輸出3。



    入度減1可以理解為將V3與當(dāng)前頂點連接的弧刪除。繼而孤立V3,也就是V3已經(jīng)遍歷過了,可以刪除。


  1. 此時棧中還有V0,V1,利用上面同樣方法針對V1操作,輸出棧頂gettop = 1, count = 2, 遍歷頂點V1 連接的弧鏈表,發(fā)現(xiàn)與V2,V4,V8頂點相連接,并將這三個頂點的入度減1,此時V2入度0,V4,V8入度為1, 因為V2入度為0,則將V2序號2入棧。


  2. 如上方式依次對棧頂頂點操作,然后入棧入度為0的頂點。得到最終結(jié)果:


    最終結(jié)果

    此時所有輸出結(jié)果為:3-->1-->2-->6-->0-->4-->5-->8-->7-->12-->9-->10-->13-->11

根據(jù)count是否等于頂點總個數(shù)來判斷是否所有的頂點都已經(jīng)輸出,等于則表示找到了拓?fù)浣Y(jié)構(gòu)。

代碼
#include "stdio.h"
#include "stdlib.h"

#include "math.h"
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 14
#define INFINITYC 65535

/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼,如OK等 */
typedef int Status;

/*鄰接矩陣結(jié)構(gòu) */
typedef struct
{
    int vexs[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;


/* 鄰接表結(jié)構(gòu)****************** */
//邊表結(jié)點
typedef struct EdgeNode
{
    //鄰接點域,存儲該頂點對應(yīng)的下標(biāo)
    int adjvex;
    //用于存儲權(quán)值,對于非網(wǎng)圖可以不需要
    int weight;
    //鏈域,指向下一個鄰接點
    struct EdgeNode *next;
}EdgeNode;

//頂點表結(jié)點
typedef struct VertexNode
{
    //頂點入度
    int in;
    //頂點域,存儲頂點信息
    int data;
    //邊表頭指針
    EdgeNode *firstedge;
}VertexNode, AdjList[MAXVEX];

//圖結(jié)構(gòu)
typedef struct
{
    AdjList adjList;
    //圖中當(dāng)前頂點數(shù)和邊數(shù)
    int numVertexes,numEdges;
}graphAdjList,*GraphAdjList;


/*1.構(gòu)成AOV網(wǎng)圖*/
void CreateMGraph(MGraph *G)/* 構(gòu)件圖 */
{
    int i, j;
    
    /* printf("請輸入邊數(shù)和頂點數(shù):"); */
    G->numEdges=MAXEDGE;
    G->numVertexes=MAXVEX;
    
    /* 初始化圖 */
    for (i = 0; i < G->numVertexes; i++)
    {
        G->vexs[i]=i;
    }
    
    /* 初始化圖 */
    for (i = 0; i < G->numVertexes; i++)
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            G->arc[i][j]=0;
        }
    }
    
    G->arc[0][4]=1;
    G->arc[0][5]=1;
    G->arc[0][11]=1;
    G->arc[1][2]=1;
    G->arc[1][4]=1;
    G->arc[1][8]=1;
    G->arc[2][5]=1;
    G->arc[2][6]=1;
    G->arc[2][9]=1;
    G->arc[3][2]=1;
    G->arc[3][13]=1;
    G->arc[4][7]=1;
    G->arc[5][8]=1;
    G->arc[5][12]=1;
    G->arc[6][5]=1;
    G->arc[8][7]=1;
    G->arc[9][10]=1;
    G->arc[9][11]=1;
    G->arc[10][13]=1;
    G->arc[12][9]=1;
    
}

/*2.將AOV網(wǎng)圖借助鄰近矩陣轉(zhuǎn)換成鄰接表結(jié)構(gòu)*/
void CreateALGraph(MGraph G,GraphAdjList *GL)
{
    int i,j;
    EdgeNode *e;
    
    //創(chuàng)建圖
    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));
    //對圖中的頂點數(shù).弧數(shù)賦值
    (*GL)->numVertexes=G.numVertexes; 
    (*GL)->numEdges=G.numEdges;
    
    //讀入頂點信息,建立頂點表
    for(i= 0;i <G.numVertexes;i++)
    {
        (*GL)->adjList[i].in=0;
        (*GL)->adjList[i].data=G.vexs[i];
        //將邊表置為空表
        (*GL)->adjList[i].firstedge=NULL;
    }
    
    //建立邊表
    for(i=0;i<G.numVertexes;i++)
    {
        for(j=0;j<G.numVertexes;j++)
        {
            if (G.arc[i][j]==1)
            {
                //創(chuàng)建空的邊表結(jié)點
                e=(EdgeNode *)malloc(sizeof(EdgeNode));
                //鄰接序號為j
                e->adjvex=j;
                // 將當(dāng)前頂點上的指向的結(jié)點指針賦值給e
                e->next=(*GL)->adjList[i].firstedge;
                //將當(dāng)前頂點的指針指向e
                (*GL)->adjList[i].firstedge=e;
                (*GL)->adjList[j].in++;
                
            }
        }
    }
}

/*3.拓?fù)渑判? 若AOV網(wǎng)圖無回路則輸出拓?fù)渑判虻男蛄胁⑶曳祷貭顟B(tài)值1,若存在回路則返回狀態(tài)值0*/
/*拓?fù)渑判?解決的是一個工程能否順序進行的問題!*/
Status TopologicalSort(GraphAdjList GL){
    
    EdgeNode *e;
    int i,k,gettop;
    //用于棧指針下標(biāo)
    int top=0;
    //用于統(tǒng)計輸出頂點的個數(shù)
    int count=0;
    
    //建棧將入度為0的頂點入棧(目的:為了避免每次查找時都要遍歷頂點表查找有沒有入度為0的頂點)
    int *stack=(int *)malloc(GL->numVertexes * sizeof(int) );
    
    //1.遍歷鄰接表-頂點表,將入度in為0的頂點入棧
    /*參考圖1> 此時stack棧中應(yīng)該成為0,1,3.即V0,V1,V3的頂點入度為0*/
    for(i = 0; i<GL->numVertexes; i++)
        //將入度為0的頂點入棧
        if(0 == GL->adjList[i].in)
            stack[++top]=i;
    printf("top = %d\n",top);
    
    //2.循環(huán)棧結(jié)構(gòu)(當(dāng)棧中有元素則循環(huán)繼續(xù))
    while(top!=0)
    {
        //出棧
        gettop=stack[top--];
        printf("%d -> ",GL->adjList[gettop].data);
        
        //輸出頂點,并計數(shù)
        count++;
        
        //遍歷與棧頂相連接的弧
        for(e = GL->adjList[gettop].firstedge; e; e = e->next)
        {
            //獲取與gettop連接的頂點
            k=e->adjvex;
            
            //1.將與gettop連接的頂點入度減1;
            //2.判斷如果當(dāng)前減1后為0,則入棧
            if( !(--GL->adjList[k].in) )
                //將k入棧到stack中,并且top加1;
                stack[++top]=k;
        }
    }
    
    /*思考:3 -> 1 -> 2 -> 6 -> 0 -> 4 -> 5 -> 8 -> 7 -> 12 -> 9 -> 10 ->13 -> 11
     這并不是唯一的拓?fù)渑判蚪Y(jié)果.
     分析算法:將入度為0的頂點入棧的時間復(fù)雜度為O(n), 而之后的while 循環(huán),每個頂點進一次棧,并且出一次棧. 入度減1, 則共執(zhí)行了e次. 那么整個算法的時間復(fù)雜度為O(n+e)*/
    
    printf("\n");
    
    //判斷是否把所有的頂點都輸出. 則表示找到了拓?fù)渑判?
    if(count < GL->numVertexes)
        return ERROR;
    else
        return OK;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 拓?fù)渑判?\n");
    MGraph G;
    GraphAdjList GL;
    int result;
    CreateMGraph(&G);
    CreateALGraph(G,&GL);
    result=TopologicalSort(GL);
    printf("result:%d",result);
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內(nèi)容