概念
設(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é)果有兩種情況:
- 如果此網(wǎng)中的全部頂點被輸出,則說明它是不存在環(huán)(回路)的
AOV網(wǎng)
。
- 如果輸出的頂點數(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的頂點。
- 創(chuàng)建一個棧,用來存儲入度in為0的頂點序號(0~13);
- 遍歷AOV圖中頂點表,判斷入度為0的頂點全部入棧。
過程
-
初始化棧,將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)遍歷過了,可以刪除。
-
此時棧中還有V0,V1,利用上面同樣方法針對V1操作,輸出棧頂gettop = 1, count = 2, 遍歷頂點V1 連接的弧鏈表,發(fā)現(xiàn)與V2,V4,V8頂點相連接,并將這三個頂點的入度減1,此時V2入度0,V4,V8入度為1, 因為V2入度為0,則將V2序號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;
}