圖的一些總結

1.圖的定義
    1)線性表咱們把數據元素叫作元素,樹中將數據元素叫結點,在圖中數據元素,咱們則稱之爲頂點。
    2)線性表中能夠沒有數據元素,稱爲空表。樹中能夠沒有結點,叫作空樹。再圖結構中,不容許沒有頂點。
    3)線性表中,相鄰的數據元素之間具備線性關係,樹結構中,相鄰兩層的結點具備層次關係,而圖中兩個頂點之間均可能有關係,頂點
      之間的邏輯關係來表示,邊集能夠是空的。
2.各類圖定義
    無向邊:若頂點v1到v1之間的邊沒有方向,則稱這條邊爲無向邊,用無序偶對來表示。若是圖中任意兩個頂點之間的邊都是無向邊,則稱爲
    無向圖。
    有向邊:若從頂點vi到vj的邊有方向,則稱這條邊爲有向邊,也稱爲弧。
    若是圖中任意兩個頂點之間的邊都是有向邊,則這個圖爲有向圖。
    *看清楚,有向邊用小括號"()"表示,而有向邊則是用尖括號"<>"表示。
    入度,出度
    連通圖,強連通
    圖按照有無方向分爲無向圖和有向圖。無向圖由頂點和邊構成,有向圖由頂點和弧構成,弧有弧頭和弧尾之分。
    圖按照邊或弧的多少分爲稀疏圖和稠密圖。若是任意兩個頂點之間都存在邊叫徹底圖。有向的叫有向徹底圖。若無重複的邊或頂點到自身的邊則叫簡單圖。
    圖中頂點之間有鄰接點。無向圖頂點的邊數叫作度,有向圖頂點分爲出度和入度。
    圖上的邊或弧上帶權則稱爲網。
    圖中頂點間存在路徑,兩頂點存在路徑則說明是連通,若是路徑最終回到起始點則稱爲環,當中不重複叫簡單路徑。若任意兩頂點都是連通的,則圖就是
    連通圖,有向則稱爲強連通。
    無向圖中連通且n個頂點n-1條邊叫生成樹。有向圖中一頂點入度爲0其他頂點入度爲1的叫有向圖。一個有向圖由若干棵有向樹構成生成森林。
2.圖的存儲結構
    1)鄰接矩陣
        考慮到圖是由頂點和邊或弧兩部分組成,合在一塊兒比較困難,那就很天然考慮到分兩個結構分別存儲。頂點不分大小、主次,須要一個二維數組來存儲。
    
        圖的鄰接矩陣存儲方式是兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱爲鄰接矩陣)存儲圖中的邊或弧的信息。
        無向圖是的邊數組是一個對稱矩陣。
        對稱矩陣
            即從矩陣的左上角到右下角的主對角軸,右上角的元與左下角相對應的元所有都是相等的。
    代碼實現:
        typedef char VertexType;/* 頂點類型由用戶自定義 */
        typedef int EdgeType;  /* 邊上的權值類型應由用戶定義 */
        #define MAXVEX  100 /* 最大頂點數,應由用戶定義*/
        #define INFINITY 65535 /* 用65535表明無窮*/
        typedef struct
        {
            VertexType vexs[MAXVEX]; /*頂點表*/
            EdgeType  arc[MAXVEX][MAXVEX];/*鄰接矩陣,可看作邊表 */
            int numVertexes, numEdges;/* 圖中當前的 頂點數 和 邊數 */
        }MGraph;
    有了這個結構定義,咱們構造一個圖,其實就是給頂點表和邊表輸入數據的過程。咱們看看無向圖的建立代碼。
        void CreateMGraph(MGraph  *G)
        {
            int i,j,k,w;
            printf("輸入頂點數和邊數:\n");
            scanf("%d,%d",&G->numVertexes,&G->numEdges);  /* 輸入頂點數和邊數 */
            for(i=0;i<G->numVertexes;i++)/* 讀入頂點信息,創建頂點表*/
                scanf("%c",&G->vexs[i]);
            for(i=0;i<G->numVertexes;i++)
                for(j=0;j<G->numVertexes;j++)
                    G->arc[i][j] = INFINITY;/*鄰接矩陣初始化*/
            for(k=0;k<G->numEdges;k++)  /**  讀入numEdges條邊,創建鄰接矩陣**/
            {
                printf("輸入邊(vi,vj)上的下標i,下標j和權w:\n");
                scanf("%d,%d,%d",&i,&j,&w);/*  輸入邊(vi,vj)上的權w */
                G->arc[i][j] = w;
                G->arc[j][i] = G->arc[i][j];/* 由於是無向圖,矩陣對稱 */
            }
            
        }
    從代碼也能夠獲得,n個頂點和e條邊的無向網圖的建立,時間複雜度爲O(n+n*n+e),其中對鄰接矩陣的初始化耗費了O(n*n)的時間。
    2)鄰接表
        鄰接矩陣是不錯的一種圖存儲結構,可是咱們發現,對於邊數相對頂點較少的圖,這種結構是存在對存儲空間的極大的浪費
    數組與鏈表相結合的存儲方法稱爲連接表。
    鄰接表的處理方法:
        1.圖中頂點用一個一維數組存儲。
        2.圖中每一個頂點vi的全部鄰接點構成一個線性表,因爲鄰接點的個數不定,因此用單鏈表存儲。
    定義結構體:
        typedef char VertexType;  /* 頂點類型應由用戶定義*/
        typedef int EdgeType;  /* 邊上的權值類型應該由用戶定義*/
        
        typedef struct EdageNode   /* 邊表結點 */
        {
            int adjvex; /* 鄰邊點域,存儲該結點對應的下標*/
            EdgeType weight;/* 用於存儲權值,對於非網圖能夠不須要*/
            struct EdgeNode *next;/* 鏈域,指向下一個鄰接點 */
        }EdgeNode;
        
        typedef struct VertexNode  /* 頂點表結點 */
        {
            VertexType data;/*  頂點域,存儲頂點信息 */
            EdgeNode *firstedge; /* 邊表頭指針 */
        }VertexNode,AdjList[MAXVEX];

        typedef struct  
        {
            AdjList adjList;
            int numVertexes,numEdges;   /* 圖中當前頂點數和邊數  */
        }GraphAdjList;
    無向圖的鄰接表建立代碼以下:(這裏沒有搞明白,研究一下頭插法)
        void CreateALGraph(GraphAdjList *G)
        {
            int i,j,k;
            EdgeNode *e;
            printf("輸入頂點數和邊數:\n");
            scanf("%d,%d",&G->numVertexes,&G->numEdges);/* 輸入頂點數和邊數 */
            for(i=0;i<G->numVertexes;i++)  /* 讀入頂點信息,創建頂點表 */
            {
                scanf(&G->adjList[i].data);  /* 輸入頂點信息  */
                G->adjList[i].firstedge = NULL;   /* 將邊表置爲空表 */
            }
            for(k=0;k<G->numEdges;k++)   /* 創建邊表*/
            {
                printf("輸入邊(vi,vj)上的頂點序號:\n");
                scanf("%d,%d",&i,&j);   /* 輸入邊(vi,vj)上的頂點序號 */
                e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間 */
                                    /* 生成邊表結點 */
                e->adjvex = j; /* 鄰接序號爲j */
                e->next = G->adjList[i].firstedge; /* 將e指針指向當前頂點指向的結點 */
                G->adjList[j].firstedge = e;  /* 將當前頂點的指針指向 e */
                
                e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間 */
                                    /* 生成邊表結點 */
                e->adjvex = i; /* 鄰接序號爲j */
                e->next = G->adjList[j].firstedge; /* 將e指針指向當前頂點指向的結點 */
                G->adjList[j].firstedge = e;  /* 將當前頂點的指針指向 e */
            }
        }
    3)十字鏈表
        十字鏈表的好處就是由於把鄰接表和逆鄰接表整合在了一塊兒,這樣既容易找到以vi爲尾的弧,也容易找到以vi爲頭的弧,因
          而容易求得頂點的出度和入度。並且它除告終構複雜
一點外,其實建立圖算法的時間複雜度和鄰接表相同,所以,再有向圖中的
          應用中,十字鏈表是很是好的數據結構模型。
    4)鄰接多重表
        鄰接多重表與鄰接表區別,僅僅是在於同一條表在鄰接表中用兩個結點表示,而在鄰接多重表中只有一個結點,這樣對邊的操做
         就方便的多了,修改指向就能夠了。
    5)邊集數組
        邊集數組是由兩個一維數組構成,一個是存儲頂點的信息;另外一個是存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、
        終點下標(end)和權(weight)組成。
            ------------------------
            |begin  |  end | weight|
            ------------------------
                  其中begin是存儲起點下標,end是存儲終點下標,weight是存儲權值
3.圖的遍歷
    從圖中某一頂點出發訪遍圖中其他頂點,且每個頂點僅被訪問一次,這個過程就要作圖的遍歷。
    對於圖的遍從來說,如何避免因迴路陷入死循環,就須要科學地設計遍歷方案,一般由兩種遍歷次序方案:深度優先和廣度優先遍歷。
    1)深度優先遍歷
        深度優先遍歷(Depth_First_Search),也有稱爲深度優先搜索,簡稱DFS。
        深度優先遍歷其實就是一個遞歸的過程。
        若是咱們用的是鄰接矩陣的方式,則代碼以下:
        typedef int Boolean;  /* Boolean是布爾類型,其值是true或者false */
        Boolean  visitad[MAX];    /* 訪問標誌的數組 */
        /* 鄰接矩陣的深度優先遞歸算法 */
        void DFS(MGraph G,int i)
        {
            int j;
            visitad[i] = TRUE;
            printf("%c", G.vexs[i]); /* 打印頂點,也能夠其餘操做 */
            for(j=0; j<G.numVertexes; j++)
                if(G.arc[i][j] == 1 && !visited[j])
                        DFS(G,j);    /* 對爲訪問的鄰接頂點遞歸調用 */
        }
        /* 鄰接矩陣的深度優先遍歷操做  */
        void DFSTraverse(MGraph G)
        {
            int i;
            for(i=0; i<G.numVertexes; i++)
                visitad[i] = FALSE;   /* 初始化全部頂點狀態都是未訪問過的狀態 */
            for(i = 0;i<G.numVertexes; i++)
                if(!visitad[i]) /* 對未訪問的頂點調用DFS,如果連通圖,只會執行一次 */
                    DFS(G,i);
        }
        鏈表實現方法:
        /************ 鄰接表的深度優先遞歸算法 *****************/
        void DFS(GraphAdjList GL, int i)
        {
            EdgeNode *p;
            visited[i] = TRUE;
            printf("%c",GL->adjList[i].data); /* 打印頂點,也能夠其餘操做 */
            p = GL->adjList[i].firstedge;
            while(p)
            {
                if(!visited[p->adjvex])
                    DFS(GL,p->adjvex);/* 對未訪問的鄰接頂點遞歸調用 */
                p= p->next;
            }
        }
        /**    鄰接表的深度遍歷操做           */
        void DFSTraverse(GraphAdjList GL)
        {
            int i;
            for(i =0;i< GL->numVertexes; i++)
                visited[i] = FALSE; /** 初始化所欲偶頂點狀態都是未訪問過的狀態 **/
            for(i =0;i<GL->numVertexes; i++)
                if(!visited[i]) /* 對未訪問過的頂點調用DFS,如果連通圖,只會執行一次 */
                    DFS(GL,i);
        }
    2)廣度優先遍歷
        廣度優先遍歷(Breadth_First_Search),又稱爲廣度優先搜索,簡稱BFS.
        鄰接矩陣結構的代碼:
        /* 鄰接矩陣的廣度遍歷算法 */
        void BFSTraverse(MGraph G)
        {
            int i,j;
            Queue Q;
            for(i=0;i<G.numVertexes;i++)
                visited[i] = FALSE;
            InitQueue(&Q);   /* 初始化一輔助用的隊列 */
            for(i=0;i<G.numVertexes;i++)/* 對每個頂點作循環 */
            {
                if(!visited[i])   /* 如果未訪問過就處理 */
                {
                    visited[i] = TRUE;/*設置當前頂點訪問過*/
                    printf("%c",G.vexs[i]); /* 打印頂點,也能夠其餘操做*/
                    EnQueue(&Q,i);/*將此頂點入隊列 */
                    while(!QueueEmpty(Q)) /*若當前隊列不爲空*/
                    {
                        DeQueue(&Q,&i); /* 將隊中元素出隊列,賦值給i */
                        for(j=0;j<G.numVertexes;j++)
                        {
                            /*判斷其餘頂點若與當前頂點存在邊且未訪問過 */
                            if(G.arc[i][j] == 1 && !visited[j])
                            {
                                visited[j] = TRUE; /* 將找到的此頂點標記爲已訪問 */
                                printf("%c ",G.vexs[j]); /* 打印頂點 */
                                EnQueue(&Q,j);    /* 將找到的此頂點入隊列 */
                            }
                        }
                    }
                }
            }
        }
        鄰接表的廣度優先遍歷,代碼與鄰接矩陣差別不大,代碼以下:
        /* 鄰接表的廣度遍歷算法 */
        void BFSTraverse(GraphAdjList GL)
        {
            int i;
            EdgeNode *p;
            Queue Q;
            for(i=0; i< GL->numVertexes; i++)
            {
                visited[i] = FALSE;
            }
            InitQueue(&Q);
            for(i = 0;i<GL->numVertexes;i++)
            {
                if(!visited[i])
                {
                    visited[i] = TRUE;
                    printf("%c",GL->adjList[i].data); /*打印頂點,也能夠其餘操做*/
                    EnQueue(&Q,i);
                    while(!QueueEmpty(Q))
                    {
                        DeQueue(&Q,&i);
                        p = GL->adjList[i].firstedge;/*找到當前頂點邊錶鏈表頭指針*/
                        while(p)
                        {
                            if(!visited[p->adjvex]) /*若此頂點未被訪問*/
                            {
                                visited[p->adjvex] = TRUE;
                                printf("%c ",GL->adjList[p->adjvex].data);
                                EnQueue(&Q,p->adjvex); /* 將此頂點入隊列*/
                            }
                            p = p->next; /* 指針指向下一個鄰接點*/
                        }
                    }
                }
            }
        }
4.最小生成樹
    構造連通網的最小代價生成樹稱爲最小生成樹。
    找連通圖的最小生成樹,經典算法有兩種,普里姆算法和克魯斯卡爾算法。
    1)普利姆(Prim)算法
        /* Prim 算法最小生成樹 */
        void MiniSpanTree_Prim(MGraph G)
        {
            int min,i,j,k;
            int adjvex[MAXVEX];/* 保存相關頂點下標 */
            int lowcost[MAXVEX]; /* 保存相關頂點間邊的權值 */
            lowcost[0] = 0; /*初始化第一個權值爲0 ,即v0加入生成樹 */
                            /*lowcost 的值爲0,在這裏就是此下標的頂點已經加入生成樹*/
            adjvex[0] = 0; /*初始化第一個頂點下標爲0*/
            for(i=1;i<G.numVertexes; i++)  /*循環除下標爲0外的所有結點*/
            {
                lowcost[i] = G.arc[0][i]; /*將v0頂點與之有邊的權值存入數組*/
                adjvex[i] = 0; /* 初始化都爲v0的下標*/
                
            }
            for(i = 0;i<G.numVertexes; i++)
            {
                if(lowcost[j] != 0 && lowcost[j] < min)
                {/*若是權值不爲0,且權值小於min */
                    min = lowcost[j]; /* 則讓當前權值成爲最小值 */
                    k = j;/*將當前權值成爲最小值*/
                }
                j++;
            }
            printf("(%d,%d)", adjvex[k],k); /*打印當前頂點邊中權值最小邊 */
            lowcost[k] = 0; /* 將當前權值設置爲 0 ,表示此頂點已經完成任務 */
            for(j = 1;j< G.numVertexes; j++)
            {
                if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
                {
                    /* 若下標爲k頂點各邊權值小於此前這些頂點未被加入生成樹權值*/
                    lowcost[j] = G.arc[k][j]; /*將較小的權值存入lowcost */
                    adjvex[j] = k;   /* 將下標爲k的頂點存入adjvex*/
                }
            }
        }
    2)克魯斯卡爾(Kruskal)算法
        普利姆(Prim)算法是以某頂點爲起點,逐步找各頂點上最小權值的邊來構建最小生成樹的。咱們能夠直接就以邊爲目標去構建,由於權值是在邊上,直接去找
    最小權值的邊來構建生成最小樹。
    edge邊集數組結構的定義代碼:
    /*對邊集數組Edge結構的定義*/
    typedef struct
    {
        int begin;
        int end;
        int weight;
    }Edge;
    克魯斯卡爾算法代碼以下,左側數字爲行號。其中MAXEDGE爲邊數量的極大值,此處大於等於15便可,MAXVEX爲頂點個數最大值,此處大於等於9便可。
    實現代碼:
    /* Kruskal 算法生成最小生成樹 */
    void MiniSpanTree_Kruskal(MGraph G)  /* 生成最小生成樹 */
    {
        int i,n,m;
        Edge edges[MAXEDGE]; /*定義邊集數組 */
        int parent[MAXVEX]; /*定義一數組用來判斷邊與邊是否造成環路 */
        /* 此處省略將鄰接矩陣G轉化爲邊集數組edges並按由小到大排序的代碼*/
        for(i = 0; i< G.numVertexes; i++)
            parent[i] = 0;  /*初始化數組值爲0 */
        for(i=0;i<G.numEdges; i++) /*循環每一條邊 */
        {
            n = Find(parent,edges[i].begin);
            m = Find(parent,edges[i].begin);
            if(n != m) /*假如n與m不等,說明此邊沒有與現有生成樹造成環路 */
            {
                parent[n] = m;  /* 將此邊的結尾頂點放入下標爲頂點的parent中*/
                                /* 表示此頂點已經在生成樹集合中*/
                printf(" (%d, %d) %d ",edges[i].begin,edges[i].end,edges[i].weight);
            }
        }
    }
    
    int Find(int *parent, int f)/* 查找連線頂點的尾部下標 */
    {
        while(parent[f] > 0)
            f = parent[f];
        return  f;
    }
5.最短路徑
        1)迪傑斯特拉(Dijkstra)算法
            按路徑長度遞增的次序產生最短路徑的算法。
            代碼實現:
            #define MAXVEX 9
            #define INFINITY 65535
            typedef int Pathmatirx[MAXVEX];  /*用於存儲最短路徑下標的數組*/
            typedef int ShortPathTable[MAXVEX];  /*用於存儲到各點最短路徑的權值和*/
            /* Dijkstra算法,求有向網G的v0頂點到其他頂點v最短路徑P[v]及帶權長度D[v]*/
            /* P[v]的值爲前驅頂點下標,D[v]表示v0到v的最短路徑長度和 */
            void ShortestPath_Dijkstra(MGraph G, int v0, Pathmatirx *P,ShortPathTable *D)
            {
                int v,w,k,min;
                int final[MAXVEX]; /* final[w] = 1 表示求得頂點v0至vw的最短路徑 */
                for(v =0;v<G.numVertexes; v++)
                {
                    final[v] = 0; /*所有頂點初始化爲未知最短路徑狀態 */
                    (*D)[v] = G.matirx[v0][v]; /* 將與v0點右連線的點加上權值 */
                    (*p)[v] = 0; /*初始化路徑數組p爲 0 */
                }
                (*D)[v0] = 0; /*v0至v0的路徑爲0 */
                final[v0] = 1; /* v0 至v0不須要路徑 */
                /* 開始主循環,每次求得v0到某個v頂點的最短路徑 */
                for(v = 1; v < G.numVertexes; v++)
                {
                    min = INFINITY;  /*當前所知離v0頂點的最近距離*/
                    for(w = 0; w<G.numVertexes; w++)  /*尋找離v0最近的頂點 */
                    {
                        if(!final[w] && (*D)[w]<min)
                        {
                            k = w;
                            min = (*D)[w]; /*w頂點離v0頂點最近*/
                        }
                    }
                    final[k] = 1; /* 將目前找到的最近的頂點置爲1 */
                    for(w= 0; w<G.numVertexes; w++)  /*修正當前最短路徑及距離*/
                    {
                        /* 若是通過v頂點的路徑比如今這條路徑的長度短的話*/
                        if(!final[w] && (min + G.matirx[k][w] < (*D)[w]))
                        {
                            /*說明了找到了更短的路徑,修改D[w]和P[w] */
                            (*D)[w] = min + G.matirx[k][w]; /* 修改當前路徑長度 */
                            (*p)[w] = k;
                        }
                    }
                }
            }
        2)弗洛伊德(Floyd)算法
            這個算法涉及到了矩陣的知識,上代碼
            typedef int Pathmatirx[MAXVEX][MAXVEX];
            typedef int ShortPathTable[MAXVEX][MAXVEX];
            /*Floyd算法,求網圖G中各頂點v到其他頂點w最短路徑P[v][w]及帶權長度D[v][w] */
            void ShortestPath_Floyd(MGraph G, Pathmatirx *p, ShortPathTable *D)
            {
                int v,w,k;
                for(v=0;v<G.numVertexes; ++v)/*初始化D與P*/
                {
                    for(w=0;w<G.numVertexes; ++w)
                    {
                        (*D)[v][w] = G.matirx[v][w]; /* D[v][w]值即爲對應點間的權值 */
                        (*P) [v][w] = w; /*初始化P */
                    }
                }
                for(k = 0; k<G.numVertexes; ++k)
                {
                    for(v = 0;v < G.numVertexes; ++v)
                    {
                        for(w = 0;w<G.numVertexes; ++w)
                        {
                            if((*D)[v][w] > (*D)[v][k] + (*D)[k][w])
                            {
                                /* 若是淨多下標爲k頂點路徑比原兩點間路徑更粗*/
                                /* 將當前兩點間權值設爲更小的一個 */
                                (*D)[v][w] = (*D)[v][w] + (*D)[k][w];
                                (*P)[v][w] = (*P)[v][k]; /*路徑設置淨多下標爲k的頂點*/
                            }
                        }
                    }
                }
            }
6.拓撲排序
    在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖爲頂點表示活動的網,咱們稱爲AOV網。
    所謂拓撲排序,其實就是對一個有向圖構造拓撲序的過程。
    1)拓撲排序算法
        基本思路:
            從AOV網中選擇一個入度爲0的頂點輸出,而後刪去此頂點,並刪除以此爲頂點爲尾的弧,繼續重複此步驟,直到輸出所有頂點
        或者AOV網中不存在入度爲0的頂點爲止。
    拓撲排序中,設計到的結構代碼以下:
    typedef struct EdgeNode    /*邊表結點*/
    {
        int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */
        int weight;   /*用於存儲權值,對於非同圖能夠不須要 */
        struct RdgeNode *next;   /* 鏈域,指向下一個鄰接點 */
    }EdgeNode;
    
    
    typedef struct VertexNode   /* 頂點表結點 */
    {
        int in;        /* 頂點入度 */
        int data;      /* 頂點域,存儲頂點信息 */
        EdgeNode *firstedge; /* 邊表頭指針 */
    }VertexNode,AdjList[MAXVEX];
    
    typedef struct
    {
        AdjList adjList;
        int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */
    }graphAdjList, *GraphAdjList;
    /* 拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回OK,如有迴路則返回ERROR */
    Status TopologicalSort(GraphAdjList GL)
    {
        EdgeNode *e;
        int i,k,gettop;
        int top = 0;  /*用於棧指針下標 */
        int count = 0; /* 用於統計輸出頂點的個數 */
        int *stack;  /* 建棧存儲入度爲0 的頂點 */
        stack = (int *)malloc(GL->numVertexes *sizeof(int));
        for(i = 0;i<GL->numVertexes; i++)
            if(GL->adjList[i].in == 0)
                    stack[++top] = i; /* 將入度爲0 */
        while(top != 0)
        {
            gettop = stack[top--]; /* 出棧 */
            printf("%d -> ",GL->adjList[gettop].data); /* 打印此頂點 */
            count++; /*統計輸出頂點數 */
            for(e = GL->adjList[gettop].firstedge; e; e=e->next)
            {/* 對此頂點弧表遍歷 */
                k = e->adjvex;
                if(!(--GL->adjList[k].in))/*將k號頂點鄰接點的入度減爲1*/
                    stack[++top] = k; /* 若爲0則入棧,以便於下次循環輸出 */
            }
        }
        
        if(count < GL->numVertexes) /* 若是count 小於頂點數 ,說明存在環 */
        {
            return ERROR;
        }
        else
            return OK;
                    
    }
7.關鍵路徑
    拓撲排序主要是爲解決一個工程可否順序進行的問題,但有時咱們還須要解決工程完成須要的最短期問題。
    在AOV網的基礎上,咱們來介紹一個新的概念。在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,
    這種有向圖的邊表示活動的網,咱們稱之爲AOE網。咱們把AOE網中沒有入邊的頂點稱爲始點或源點,沒有出邊的頂點稱爲終點或匯點。
    須要幾個參數:
        前兩個事件是預計:
        1.事件的最先發生時間etv(earlist time of vertex):即頂點vk的最先發生時間。
        2.事件的最晚發生時間ltv(latest time of vertex):即頂點vk的最晚發生時間,也就是每一個頂點對應的事件最晚須要開始的時間,超過此時間將會延誤整個工期。
        這兩個事件開始作的時間預計:
        3.活動的最先開工時間ete (earlist time of edge):即弧ak的最晚發生時間,也就是不推遲工期的最晚開工時間。
        4.活動的最晚開工時間lte(latest time of edge):即弧ak的最晚發生時間,也就是不推遲工期的最晚開工時間。
    關鍵路徑算法:
        將AOE網轉化爲鄰接表結構,注意與拓撲排序時鄰接表結構不一樣的地方在於,這裏弧鏈表增長了weight域,用來存儲弧的權值。
        首先聲明幾個全局變量:
        int *etv,*ltv;  /* 事件最先發生時間和最遲發生時間數組 */
        int *stack2;   /* 用於存儲拓撲序列的棧 */
        int top2;     /* 用於stack2的指針 */
        
        /* 拓撲排序,用於關鍵路徑計算 */
        Status TopologicalSort(GraphAdjList  GL)
        {
            EdgeNode  *e;
            int i,k,gettop;
            int top = 0; /* 用於棧指針下標 */
            int count;  /* 用於統計輸出頂點的個數 */
            int *stack;  /* 建棧將入度爲0的頂點入棧 */
            stack = (int *)malloc(GL->numVertexes *sizeof(int));
            for(i=0; i<GL->numVertexes; i++)
                if(0 == GL->adjList[i].in)
                    stack[++top] = i;
            top2 = 0;   /*初始化爲0 */
            etv = (int*)malloc(GL->numVertexes * sizeof(int)); /* 時間最先發生時間 */
            for(i=0; i<GL->numVertexes; i++)
                etv[i] = 0;  /* 初始化爲0 */
            stack2 = (int*)malloc(GL->numVertexes * sizeof(int));  /* 初始化 */
            while(top != 0)
            {
                gettop = stack[top--];
                count++;
                stack2[++top2] = gettop; /* 將彈出的頂點序號壓入拓撲序列的棧 */
                
                for(e = GL->adjList[gettop].firstedge;e;e = e->next)
                {
                    k = e->adjvex;
                    if(!(--GL->adjList[k].in))
                        stack[++top] = k;
                    if((etv[gettop]+ e->weight)>etv[k]) /* 求各頂點事件最先發生時間值 */
                        etv[k] = etv[gettop] + e->weight;
                }
            }
            if(count < GL->numVertexes)
                return ERROR;
            else
                return OK;
        }
    
    算法

相關文章
相關標籤/搜索