5. 圖 (算法和數據結構筆記)

5. 圖 算法

● 圖的存儲結構 數組

 

① //圖的數組 (鄰接矩陣 ) 存儲結構----有向圖, 無向圖, 網絡

思路: 用兩個數組來表示圖 函數

① 一個一維數組存儲圖中頂點信息; ② 一個二維數組(稱爲鄰接矩陣----是一個對稱矩陣,矩陣的元知足aij=aji)存儲圖中的邊或弧的信息。spa

 

//起始部分 指針

#include "stdio.h" blog

#include "stdlib.h" 排序

#include "io.h" 遞歸

#include "math.h" 隊列

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

#define MAXVEX 100 /* 最大頂點數,應由用戶定義 */

#define INFINITY 65535

 

//用頂點數組和邊數組(鄰接矩陣)定義表結構

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

typedef char VertexType; /* 頂點類型應由用戶定義 */

typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */

typedef struct

{

    VertexType vexs[MAXVEX]; /* 頂點表 */

    EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表, 這裏用edge這個標識符更準確 */

    int numNodes, numEdges; /* 圖中當前的頂點數和邊數 */

}MGraph;

 

/* 創建無向網圖的鄰接矩陣表示 */

void CreateMGraph(MGraph *G)

{

    int i,j,k,w;

    printf("輸入頂點數和邊數:\n");

    scanf("%d,%d",&G->numNodes,&G->numEdges); /* 輸入頂點數和邊數 */

    for(i = 0;i <G->numNodes;i++) /* 讀入頂點信息,創建頂點表 */

        scanf(&G->vexs[i]);

    for(i = 0;i <G->numNodes;i++)

        for(j = 0;j <G->numNodes;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]; /* 由於是無向圖,矩陣對稱 */

    }

}

 

//主函數

int main(void)

{

    MGraph Graph;

    CreateMGraph(&Graph);

    

    return 0;

}

對於有向圖, 咱們要先檢索鄰接矩陣的行, 再檢索列; 對於無向圖, 先檢索行或列均可以.

 

 

② //鄰接表----有向圖, 無向圖,

//思路:

① 頂點用一維數組來儲存存儲數組, 這個數組是一個結構體數組, 每一個結構體數組的份量包括頂點的數據(data)和邊表頭結點的指針(firstedge), 指向該頂點的第一個鄰接點;

② 圖中每一個頂點的全部鄰接點構成一個鏈表, 邊界結點

 

※ 鄰接矩陣是惟一的(行列號與頂點編號一致),但鄰接表不惟一(連接次序與頂點編號無關)。

//鄰接表&逆鄰接表

鄰接表: 把頂點當弧尾(肯定頂點的出度)

逆鄰接表: 把頂點當弧頭(肯定頂點的入度)

 

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

#define MAXVEX 100 /* 最大頂點數,應由用戶定義 */

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,OK*/

typedef char VertexType; /* 頂點類型應由用戶定義 */

typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */

 

/*  表 結點 */

typedef struct EdgeNode

{

    int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */

    EdgeType info;        /* 用於存儲權值,對於非網圖能夠不須要 */

    struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */

}EdgeNode;

 

/* 頂點 表 結點 */

typedef struct VertexNode

{

    VertexType data; /* 頂點域,存儲頂點信息 */

    EdgeNode *firstedge;/* 邊表頭指針 */

}VertexNode, AdjList[MAXVEX];

 

//整個鄰接表

typedef struct

{

    AdjList adjList;

    int numNodes,numEdges; /* 圖中當前頂點數和邊數 */

}GraphAdjList;

 

/* 創建圖的鄰接表結構 */

void CreateALGraph(GraphAdjList *G)

{

    int i,j,k;

    EdgeNode *e;

    printf("輸入頂點數和邊數:\n");

    scanf("%d,%d",&G->numNodes,&G->numEdges); /* 輸入頂點數和邊數 */

    for(i = 0;i < G->numNodes;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[i].firstedge=e;        /* 將當前頂點的指針指向e */

        

        e=(EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間,生成邊表結點 */

        e->adjvex=i;                    /* 鄰接序號爲i */

        e->next=G->adjList[j].firstedge;    /* e的指針指向當前頂點上指向的結點 */

        G->adjList[j].firstedge=e;        /* 將當前頂點的指針指向e */

    }

}

 

//主函數

int main(void)

{

    GraphAdjList Graph;

    CreateALGraph(&Graph);

    

    return 0;

}

 

③ //十字鏈表----有向圖: 結合鄰接表和逆鄰接表

(從新定義頂點表結點結構和邊表結點結構)

(1)頂點表結點,設3個域(每一個頂點也是一個數據元素)

data: 頂點信息

firstin: 以該頂點爲弧頭的第一條弧結點

firstout: 以該頂點爲弧尾的第一條弧結點

 

(2)邊表結點,設5個域(每段弧是一個數據元素)

tailvex: 弧尾頂點位置

headvex: 弧頭頂點位置

hlink: 指向和這條弧的弧頭相同的下一條弧的位置

tlink: 指向和這條弧的弧尾相同的下一弧弧的位置

info: 弧信息

 

④ //鄰接多重表----無向圖

(1)頂點表結點

data: 存儲頂點信息

firstedge: 指向依附於該頂點的第一條邊結點

 

(2)邊表結點

mark: 標誌域,標記該邊是否被搜索過。

ivex, jvex : 該邊依附的兩個頂點的位置

ilink: 指向下一條依附頂點 ivex 的邊

Jlink: 指向下一條依附頂點 jvex 的邊

info: 邊信息

 

⑤ //邊集數組----有向圖

邊集數組是由兩個一維數組構成。一個是存儲頂點的信息;另外一個是存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、終點下標(end)和權(weight)組成.

邊數組

 

深度優先遍歷(DFS)----前序遍歷

找鑰匙: ①從任意一個房間開始, 翻個底朝天; ②換到下一間, 再翻個底朝天......

右手原則: 在沒有碰到重複頂點的狀況下, 分叉路口始終是向右手邊走.(固然也能夠遵循"左手原則"來遍歷)

 

廣度優先遍歷(BFS)----前序遍歷

找鑰匙: ①查看全部房間裏最顯眼的位置; ②查看全部房間裏此顯眼的位置......

(深度優先遍歷和廣度優先遍歷的路徑都不惟一)

 

 

鄰接矩陣----深度和廣度遍歷

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */

typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */

 

typedef char VertexType; /* 頂點類型應由用戶定義 */

typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */

 

#define MAXSIZE 9 /* 存儲空間初始分配量 */

#define MAXEDGE 15

#define MAXVEX 9

#define INFINITY 65535

 

//圖的結構定義----用鄰接矩陣

typedef struct

{

    VertexType vexs[MAXVEX]; /* 頂點表 */

    EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表 */

    int numVertexes, numEdges; /* 圖中當前的頂點數和邊數 */ 

}MGraph;

 

/* 用到的隊列結構與函數 */

/* 循環隊列的順序存儲結構 */

typedef struct

{

    int data[MAXSIZE];

    int front;     /* 隊頭指針 */

    int rear;        /* */

}Queue;

 

/* 初始化一個空隊列Q */

Status InitQueue(Queue *Q)

{

    Q->front=0;

    Q->rear=0;

    return OK;

}

 

/* 若隊列Q爲空隊列,則返回TRUE,不然返回FALSE */

Status QueueEmpty(Queue Q)

{

    if(Q.front==Q.rear) /* 隊列空的標誌 */

        return TRUE;

    else

        return FALSE;

}

 

/* 若隊列未滿,則插入元素eQ新的隊尾元素 */

Status EnQueue(Queue *Q,int e)

{

    if ((Q->rear+1)%MAXSIZE == Q->front)    /* 隊列滿的判斷 */

        return ERROR;

    Q->data[Q->rear]=e;            /* 將元素e賦值給隊尾 */

    Q->rear=(Q->rear+1)%MAXSIZE;/* rear指針向後移一位置, */

                                /* 若到最後則轉到數組頭部 */

    return OK;

}

 

/* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */

Status DeQueue(Queue *Q,int *e)

{

    if (Q->front == Q->rear)            /* 隊列空的判斷 */

        return ERROR;

    *e=Q->data[Q->front];                /* 將隊頭元素賦值給e */

    Q->front=(Q->front+1)%MAXSIZE;    /* front指針向後移一位置, */

                                    /* 若到最後則轉到數組頭部 */

    return OK;

}

 

//建立圖的函數

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    G->numEdges=15;

    G->numVertexes=9;

 

    /* 讀入頂點信息,創建頂點表 */

    G->vexs[0]='A';

    G->vexs[1]='B';

    G->vexs[2]='C';

    G->vexs[3]='D';

    G->vexs[4]='E';

    G->vexs[5]='F';

    G->vexs[6]='G';

    G->vexs[7]='H';

    G->vexs[8]='I';

 

/* 初始化圖, 一行一行地將各元素初始化爲0 */

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */

    {

        for ( j = 0; j < G->numVertexes; j++)

        {

            G->arc[i][j]=0;

        }

    }

 

//將結點之間有邊的數組元素從新賦值, 下面構造的是對稱矩陣的左半部分

    G->arc[0][1]=1;

    G->arc[0][5]=1;

 

    G->arc[1][2]=1;

    G->arc[1][8]=1;

    G->arc[1][6]=1;

    

    G->arc[2][3]=1;

    G->arc[2][8]=1;

    

    G->arc[3][4]=1;

    G->arc[3][7]=1;

    G->arc[3][6]=1;

    G->arc[3][8]=1;

 

    G->arc[4][5]=1;

    G->arc[4][7]=1;

 

    G->arc[5][6]=1;

    

    G->arc[6][7]=1;

 

    //下面的操做是爲了造成一個對稱矩陣, 下面構造的是對稱矩陣的右半部分

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* 鄰接矩陣的深度優先遞歸算法 */

Boolean visited[MAXVEX]; /* 訪問標誌的數組, 類型爲Boolean, 包括TRUEFALSE兩個值 */

 

void DFS(MGraph G, int i) //DFS會被自身和DFSTraverse()函數調用, i是遍歷開始的頂點序號

{

     visited[i] = TRUE;    //說明當前頂點已被訪問

     printf("%c ", G.vexs[i]);/* 打印頂點,也能夠是其它操做 */

int j;

    for(j = 0; j < G.numVertexes; j++)

        if(G.arc[i][j] == 1 && !visited[j]) // visited[j]==0

             DFS(G, j);/* 對爲訪問的鄰接頂點遞歸調用 */

}

//在本程序中, 會先執行下面的DFSTraverse()函數, 此時i=0, 而後DFSTraverse()函數會調用DFS()函數, 此時, 0號頂點開始遍歷, 而後DFS會遞歸調用自身, 每遍歷一個頂點, visited[i] = TRUE;這一句都會執行, 代表該頂點已被訪問.

 

/* 鄰接矩陣的深度遍歷操做 */

void DFSTraverse(MGraph G)

{

    int i;

     for(i = 0; i < G.numVertexes; i++)

         visited[i] = FALSE; /* 初始全部頂點狀態都是未訪問過狀態 */

    for(i = 0; i < G.numVertexes; i++)

         if(!visited[i]) /* 對未訪問過的頂點調用DFS如果連通圖,只會執行一次 */ 

            DFS(G, i);

}

 

/* 鄰接矩陣的廣度遍歷算法 */

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]) //若是 G.arc[i][j]這條邊存在

                    {

                         visited[j]=TRUE;            /* 將找到的此頂點標記爲已訪問 */

                        printf("%c ", G.vexs[j]);    /* 打印頂點 */

                        EnQueue(&Q,j);                /* 將找到的此頂點入隊列 */

                    }

                }

            }

        }

    }

}

 

//主函數

int main(void)

{

    MGraph Graph;

    CreateMGraph(&Graph);

    printf("\n深度遍歷:");

    DFSTraverse(Graph);

    printf("\n廣度遍歷:");

    BFSTraverse(Graph);

    return 0;

}

 

鄰接表----深度和廣度遍歷

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

#define MAXSIZE 9 /* 存儲空間初始分配量 */

#define MAXEDGE 15

#define MAXVEX 9

#define INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */

typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */

 

typedef char VertexType; /* 頂點類型應由用戶定義 */

typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */

 

/* 鄰接矩陣結構 */

typedef struct

{

    VertexType vexs[MAXVEX]; /* 頂點表 */

    EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表 */

    int numVertexes, numEdges; /* 圖中當前的頂點數和邊數 */ 

}MGraph;

 

/* 鄰接表結構 */

typedef struct EdgeNode /* 邊表結點 */ 

{

    int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */

    int weight;        /* 用於存儲權值,對於非網圖能夠不須要 */

    struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ 

}EdgeNode;

 

/* 頂點表結點 */

typedef struct VertexNode

{

    int in;    /* 頂點入度 */

    char data; /* 頂點域,存儲頂點信息 */

    EdgeNode *firstedge;/* 邊表頭指針 */

}VertexNode, AdjList[MAXVEX];

 

/* 邊表結點 */

typedef struct

{

    AdjList adjList;

    int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */

}graphAdjList,*GraphAdjList;

/* **************************** */

 

/* 用到的隊列結構與函數 */

/* 循環隊列的順序存儲結構 */

typedef struct

{

    int data[MAXSIZE];

    int front;     /* 頭指針 */

    int rear;        /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */

}Queue;

 

/* 初始化一個空隊列Q */

Status InitQueue(Queue *Q)

{

    Q->front=0;

    Q->rear=0;

    return OK;

}

 

/* 若隊列Q爲空隊列,則返回TRUE,不然返回FALSE */

Status QueueEmpty(Queue Q)

{

    if(Q.front==Q.rear) /* 隊列空的標誌 */

        return TRUE;

    else

        return FALSE;

}

 

/* 若隊列未滿,則插入元素eQ新的隊尾元素 */

Status EnQueue(Queue *Q,int e)

{

    if ((Q->rear+1)%MAXSIZE == Q->front)    /* 隊列滿的判斷 */

        return ERROR;

    Q->data[Q->rear]=e;            /* 將元素e賦值給隊尾 */

    Q->rear=(Q->rear+1)%MAXSIZE;/* rear指針向後移一位置, */

                                /* 若到最後則轉到數組頭部 */

    return OK;

}

 

/* 若隊列不空,則刪除Q中隊頭元素,e返回其值 */

Status DeQueue(Queue *Q,int *e)

{

    if (Q->front == Q->rear)            /* 隊列空的判斷 */

        return ERROR;

    *e=Q->data[Q->front];                /* 將隊頭元素賦值給e */

    Q->front=(Q->front+1)%MAXSIZE;    /* front指針向後移一位置, */

                                    /* 若到最後則轉到數組頭部 */

    return OK;

}

 

//建立圖的函數

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    G->numEdges=15;

    G->numVertexes=9;

 

    /* 讀入頂點信息,創建頂點表 */ 

    G->vexs[0]='A';

    G->vexs[1]='B';

    G->vexs[2]='C';

    G->vexs[3]='D';

    G->vexs[4]='E';

    G->vexs[5]='F';

    G->vexs[6]='G';

    G->vexs[7]='H';

    G->vexs[8]='I';

 

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */

    {

        for ( j = 0; j < G->numVertexes; j++)

        {

            G->arc[i][j]=0;

        }

    }

 

    G->arc[0][1]=1;

    G->arc[0][5]=1;

 

    G->arc[1][2]=1;

    G->arc[1][8]=1;

    G->arc[1][6]=1;

    

    G->arc[2][3]=1;

    G->arc[2][8]=1;

    

    G->arc[3][4]=1;

    G->arc[3][7]=1;

    G->arc[3][6]=1;

    G->arc[3][8]=1;

 

    G->arc[4][5]=1;

    G->arc[4][7]=1;

 

    G->arc[5][6]=1;

    

    G->arc[6][7]=1;

 

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* 利用鄰接矩陣構建鄰接表 */

void CreateALGraph(MGraph G,GraphAdjList *GL)

{

    int i,j;

    EdgeNode *e;

 

    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));

 

    (*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)

            {

                e=(EdgeNode *)malloc(sizeof(EdgeNode));

                e->adjvex=j;                    /* 鄰接序號爲j */

                e->next=(*GL)->adjList[i].firstedge;    /* 將當前頂點上的指向的結點指針賦值給e */

                (*GL)->adjList[i].firstedge=e;        /* 將當前頂點的指針指向e */

                (*GL)->adjList[j].in++;

                

            }

        }

    }

    

}

 

/* 鄰接表的深度優先遞歸算法 */

Boolean visited[MAXSIZE]; /* 訪問標誌的數組 */

 

void DFS(GraphAdjList GL, int i)

{

     visited[i] = TRUE;

     printf("%c ",GL->adjList[i].data);/* 打印頂點,也能夠其它操做 */

    EdgeNode *p;

    p = GL->adjList[i].firstedge;

    while(p) // while(p!=0)

    {

         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);

}

 

/* 鄰接表的廣度遍歷算法 */

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;    /* 指針指向下一個鄰接點 */

                }

            }

        }

    }

}

 

//主函數

int main(void)

{

    MGraph Graph;

    GraphAdjList GL;

    CreateMGraph(&Graph);

    CreateALGraph(Graph,&GL);

 

    printf("\n深度遍歷:");

    DFSTraverse(GL);

    printf("\n廣度遍歷:");

    BFSTraverse(GL);

    return 0;

}

 

 

最小生成樹普里姆(Prim)算法

Prim算法特色(對頂點操做): 頂點歸併,與邊數無關,適於稠密網。

普里姆算法的基本思想: 在生成樹的構造過程當中,圖中 n 個頂點分屬兩個集合:已落在生成樹上的頂點集 U (其序號爲下面代碼中的數組int adjvex[MAXVEX]的元素)和還沒有落在生成樹上的頂點集V-U ,則應在全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊(該邊的權值爲下面代碼中的數組int lowcost[MAXVEX]的元素)

步驟:

設N=(V,{E})是連通網,TEN上最小生成樹中邊的集合:

(1)初始令U={u0},(u0屬於V), TE=NULL

(2)在全部u屬於U,v屬於V-U的邊(u,v)屬於E中,找一條代價最小的邊(u0,v0)

(3)將(u0,v0)併入集合TE,同時v0併入U

(4)重複上述操做直至U=V爲止,則T=(V,{TE})N的最小生成樹

① 初始化兩個數組,一個用來存放邊的起始點,一個用於存放邊權值

② 賦初值,從0開始,全部邊的權值都和v0相關,全部邊權值的起始點都是v0

③.大循環1MG.numVertexes-1

④ 遍歷邊權值數組,找當前數組中的最小值,並將發現的能構成最小權值的頂點的下標存入k, 以待使用

⑤ 打印邊

⑥ 將加入的頂點的lowcost設置爲0

⑦ 使用新加入的頂點的鄰接矩陣來更新lowcost數組和adjvex數組

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

#define MAXEDGE 20

#define MAXVEX 20

#define INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

//圖的結構定義

typedef struct

{

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    /* printf("請輸入邊數和頂點數:"); */

    G->numEdges=15;

    G->numVertexes=9;

 

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */

    {

        for ( j = 0; j < G->numVertexes; j++)

        {

            if (i==j)

                G->arc[i][j]=0;

            else

                G->arc[i][j] = G->arc[j][i] = INFINITY;

        }

    }

 

    G->arc[0][1]=10;

    G->arc[0][5]=11;

    G->arc[1][2]=18;

    G->arc[1][8]=12;

    G->arc[1][6]=16;

    G->arc[2][8]=8;

    G->arc[2][3]=22;

    G->arc[3][8]=21;

    G->arc[3][6]=24;

    G->arc[3][7]=16;

    G->arc[3][4]=20;

    G->arc[4][7]=7;

    G->arc[4][5]=26;

    G->arc[5][6]=17;

    G->arc[6][7]=19;

 

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* Prim算法生成最小生成樹 */

void MiniSpanTree_Prim(MGraph G)

{

    int min, i, j, k;

    int adjvex[MAXVEX];    /*已落在生成樹上的頂點集 U中各頂點的序號 */

    int lowcost[MAXVEX];/*全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊的權值*/

    

    adjvex[0] = 0;            /* 初始化第一個頂點下標爲0 */

    lowcost[0] = 0; /* lowcost的值爲0, 由於一個頂點到其自身的權值爲0 */

 

    for(i = 1; i < G.numVertexes; i++)    /* 循環除下標爲0之外的所有頂點 */

    {

        adjvex[i] = 0;                    /* 暫時將U中各頂點的序號初始化爲0 */

        lowcost[i] = G.arc[0][i];    /* 將鄰接矩陣第0行全部權值先加入數組 */

    }

    for(i = 1; i < G.numVertexes; i++)

    //注意, 下面有兩個內循環

    {

        min = INFINITY;    /*初始化最小權值爲∞, 一般設置爲不可能的大數字如3276七、65535等 */

        j = 1;

        k = 0;

        while(j < G.numVertexes)    /* 循環所有頂點 */

        {//找出lowcost數組已存儲的最小權值

            if(lowcost[j]!=0 && lowcost[j] < min)/* 若是權值不爲0且權值小於min */

            {    

                min = lowcost[j];    /* 則讓當前權值成爲最小值 */

                k = j;            /* 將發現的能構成最小權值的頂點的下標存入k, 以待使用 */

            }

            j++;

        }

        printf("(%d, %d)\n", 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 */

            }

        }

    }

}

 

//主函數

int main(void)

{

    MGraph G;

    CreateMGraph(&G);

    MiniSpanTree_Prim(G);

 

    return 0;

}

 

咱們也能夠設置一個輔助的結構體數組closedge:

struct {

VertexType adjvex; // 已落在生成樹上的頂點集 U中各頂點的序號

VRType lowcost; // 全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊的權值

} closedge[MAX_VERTEX_NUM];

 

初始狀態:V是全部頂點的集合,即V={A,B,C,D,E,F,G}UT都是空!

第1步:將頂點A加入到U中。

此時,U={A}

第2步:將頂點B加入到U中。

上一步操做以後,U={A}, V-U={B,C,D,E,F,G};所以,邊(A,B)的權值最小。將頂點B添加到U中;此時,U={A,B}

第3步:將頂點F加入到U中。

上一步操做以後,U={A,B}, V-U={C,D,E,F,G};所以,邊(B,F)的權值最小。將頂點F添加到U中;此時,U={A,B,F}

第4步:將頂點E加入到U中。

上一步操做以後,U={A,B,F}, V-U={C,D,E,G};所以,邊(F,E)的權值最小。將頂點E添加到U中;此時,U={A,B,F,E}

第5步:將頂點D加入到U中。

上一步操做以後,U={A,B,F,E}, V-U={C,D,G};所以,邊(E,D)的權值最小。將頂點D添加到U中;此時,U={A,B,F,E,D}

第6步:將頂點C加入到U中。

上一步操做以後,U={A,B,F,E,D}, V-U={C,G};所以,邊(D,C)的權值最小。將頂點C添加到U中;此時,U={A,B,F,E,D,C}

第7步:將頂點G加入到U中。

上一步操做以後,U={A,B,F,E,D,C}, V-U={G};所以,邊(F,G)的權值最小。將頂點G添加到U中;此時,U=V

 

此時,最小生成樹構造完成!它包括的頂點依次是:A B F E D C G

 

 

最小生成樹----克魯斯卡爾(Kruskal)算法

Kruskal算法特色(對邊操做)邊歸併,適於求稀疏網。

克魯斯卡爾算法的基本思想:

考慮問題的出發點: 爲使生成樹上邊的權值之和達到最小,則應使生成樹中每一條邊的權值儘量地小。

能夠看出Kruskal算法屬於一種貪心算法,並且能保證找到全局最優解。

步驟:

設連通網N=(V,{E}),令最小生成樹

(1)初始狀態爲只有n個頂點而無邊的非連通圖T=(V,{NULL})--森林,每一個頂點自成一個連通份量

(2)在E中選取代價最小的邊,若該邊的兩個頂點不在同一個圖中(即沒有造成迴路),則將此邊加入到T中;不然,捨去此邊,選取下一條代價最小的邊

(3)依此類推,直至T中全部頂點都在同一連通份量上爲止

 

圖中先將每一個頂點看做獨立的子圖,而後查找最小權值邊,這條邊是有限制條件的,邊的兩個頂點必須不在同一個圖中(不然造成迴路, 這就不是連通圖的極小連通子圖(即生成樹).),如上圖,第一個圖中找到最小權值邊爲(v1v3),且知足限制條件,繼續查找到邊(v4v6),(v2v5),(v3v6),當查找到最後一條邊時,僅僅只有(v2v3)知足限制條件,其餘的如(v3v4),(v1v4)都在一個子圖裏面,不知足條件,至此已經找到最小生成樹的全部邊。

 

① 定義一個邊集,一個頂點集, 邊集按照權的大小排列,將結點集所有初始化爲0

② 循環邊集中的每一條邊, 用定義的Find()函數檢查每條邊的起點和終點能走到的最遠的結點在哪裏,若是相同就繼續循環,不然打印並修改parent數組

 

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

#define MAXEDGE 20

#define MAXVEX 20

#define INFINITY 65535

 

//圖的結構

typedef struct

{

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

/* 對邊集數組Edge結構的定義 */

typedef struct

{

    int begin;

    int end;

    int weight;

}Edge;

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    /* printf("請輸入邊數和頂點數:"); */

    G->numEdges=15;

    G->numVertexes=9;

 

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */

    {

        for ( j = 0; j < G->numVertexes; j++)

        {

            if (i==j)

                G->arc[i][j]=0;

            else

                G->arc[i][j] = G->arc[j][i] = INFINITY;

        }

    }

 

    G->arc[0][1]=10;

    G->arc[0][5]=11;

    G->arc[1][2]=18;

    G->arc[1][8]=12;

    G->arc[1][6]=16;

    G->arc[2][8]=8;

    G->arc[2][3]=22;

    G->arc[3][8]=21;

    G->arc[3][6]=24;

    G->arc[3][7]=16;

    G->arc[3][4]=20;

    G->arc[4][7]=7;

    G->arc[4][5]=26;

    G->arc[5][6]=17;

    G->arc[6][7]=19;

 

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* 交換權值 以及頭和尾 */

void Swapn(Edge *edges,int i, int j)

{

    int temp;

    temp = edges[i].begin;

    edges[i].begin = edges[j].begin;

    edges[j].begin = temp;

    temp = edges[i].end;

    edges[i].end = edges[j].end;

    edges[j].end = temp;

    temp = edges[i].weight;

    edges[i].weight = edges[j].weight;

    edges[j].weight = temp;

}

 

/* 對權值進行排序 */

void sort(Edge edges[],MGraph *G)

{

    int i, j;

    for ( i = 0; i < G->numEdges; i++)

    {

        for ( j = i + 1; j < G->numEdges; j++)

        {

            if (edges[i].weight > edges[j].weight)

            {

                Swapn(edges, i, j);

            }

        }

    }

    printf("權排序以後的爲:\n");

    for (i = 0; i < G->numEdges; i++)

    {

        printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);

    }

 

}

 

/* 查找連線頂點的尾部下標 */

int Find(int *parent, int f) //查找從f這個頂點開始能走到的最遠的頂點是哪裏

{//若是最後走到的最遠的頂點相同,說明是有環路生成的

    while ( parent[f] > 0)

    {

        f = parent[f];

    }

    return f;

}

 

/* 生成最小生成樹 */

void MiniSpanTree_Kruskal(MGraph G)

{

    int i, j, n, m;

    int k = 0;

    

    Edge edges[MAXEDGE];/* 定義邊集數組, 它是一個結構體數組*/ 

    int parent[MAXVEX];/* 定義一數組用來判斷邊與邊是否造成環路 */

 

    /* 用來構建邊集數組並排序********************* */

    for ( i = 0; i < G.numVertexes-1; i++)

    {

        for (j = i + 1; j < G.numVertexes; j++)

        {

            if (G.arc[i][j]<INFINITY)

            {

                edges[k].begin = i;

                edges[k].end = j;

                edges[k].weight = G.arc[i][j];

                k++;

            }

        }

    }

    sort(edges, &G);

    /* ******************************************* */

 

 

    for (i = 0; i < G.numVertexes; i++)

        parent[i] = 0;    /* 初始化數組值爲0 */

 

    printf("打印最小生成樹:\n");

    for (i = 0; i < G.numEdges; i++)    /* 循環每一條邊 */

    {

        n = Find(parent,edges[i].begin);

        m = Find(parent,edges[i].end);

        if (n != m) /* 假如nm不等,說明此邊沒有與現有的生成樹造成環路 */

        {

            parent[n] = m;    // parent數組下標記錄了邊起點,數組元素記錄了邊終點            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);

        }

    }

}

 

//主函數

int main(void)

{

    MGraph G;

    CreateMGraph(&G);

    MiniSpanTree_Kruskal(G);

    return 0;

}

 

用數組R保存最小生成樹結果

 

第1步:將邊<E,F>加入R中。 

邊<E,F>的權值最小,所以將它加入到最小生成樹結果R中。 

第2步:將邊<C,D>加入R中。 

上一步操做以後,邊<C,D>的權值最小,所以將它加入到最小生成樹結果R中。 

第3步:將邊<D,E>加入R中。 

上一步操做以後,邊<D,E>的權值最小,所以將它加入到最小生成樹結果R中。 

第4步:將邊<B,F>加入R中。 

上一步操做以後,邊<C,E>的權值最小,但<C,E>會和已有的邊構成迴路;所以,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。 

第5步:將邊<E,G>加入R中。 

上一步操做以後,邊<E,G>的權值最小,所以將它加入到最小生成樹結果R中。 

第6步:將邊<A,B>加入R中。 

上一步操做以後,邊<F,G>的權值最小,但<F,G>會和已有的邊構成迴路;所以,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。

 

此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。

 

單源最短路徑(一頂點到其他各頂點)——用Dijkstra(迪傑斯特拉)算法

全部頂點間的最短路徑(任意兩頂點之間)——用Floyd(弗洛伊德)算法

 

最短路徑—Dijkstra算法

步驟:

(1) 初始時,S只包含起點sU包含除s外的其餘頂點,且U中頂點的距離爲"起點s到該頂點的距離"[例如,U中頂點v的距離爲(s,v)的長度,而後sv不相鄰,則v的距離爲∞]

(2) U中選出"距離最短的頂點k",並將頂點k加入到S中;同時,從U中移除頂點k

(3) 更新U中各個頂點到起點s的距離。之因此更新U中頂點的距離,是因爲上一步中肯定了k是求出最短路徑的頂點,從而能夠利用k來更新其它頂點的距離;例如,(s,v)的距離可能大於(s,k)+(k,v)的距離。

(4) 重複步驟(2)(3),直到遍歷完全部頂點。

 

初始狀態:S是已計算出最短路徑的頂點集合,U是未計算除最短路徑的頂點的集合! 

第1步:將頂點D加入到S中。 

此時,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起點D的距離是3。

第2步:將頂點C加入到S中。 

上一步操做以後,U中頂點C到起點D的距離最短;所以,將C加入到S中,同時更新U中頂點的距離。以頂點F爲例,以前F到D的距離爲∞;可是將C加入到S以後,F到D的距離爲9=(F,C)+(C,D)。 

此時,S={D(0),C(3)}, U={A(∞),B(23),E(4),F(9),G(∞)}。

第3步:將頂點E加入到S中。 

上一步操做以後,U中頂點E到起點D的距離最短;所以,將E加入到S中,同時更新U中頂點的距離。仍是以頂點F爲例,以前F到D的距離爲9;可是將E加入到S以後,F到D的距離爲6=(F,E)+(E,D)。 

此時,S={D(0),C(3),E(4)}, U={A(∞),B(23),F(6),G(12)}。

第4步:將頂點F加入到S中。 

此時,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。

 

第5步:將頂點G加入到S中。 

此時,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。

第6步:將頂點B加入到S中。 

此時,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。

第7步:將頂點A加入到S中。 

此時,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。

 

此時,起點D到各個頂點的最短距離就計算出來了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

#define MAXEDGE 20

#define MAXVEX 20

#define INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

typedef struct

{

    int vexs[MAXVEX];

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

typedef int Patharc[MAXVEX]; /* 數組元素爲最短路徑下標 */

typedef int ShortPathTable[MAXVEX];/* 數組元素爲到各點的最短路徑的權值和 */

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    /* printf("請輸入邊數和頂點數:"); */

    G->numEdges=16;

    G->numVertexes=9;

 

    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++)

        {

            if (i==j)

                G->arc[i][j]=0;

            else

                G->arc[i][j] = G->arc[j][i] = INFINITY;

        }

    }

 

    G->arc[0][1]=1;

    G->arc[0][2]=5;

    G->arc[1][2]=3;

    G->arc[1][3]=7;

    G->arc[1][4]=5;

 

    G->arc[2][4]=1;

    G->arc[2][5]=7;

    G->arc[3][4]=2;

    G->arc[3][6]=3;

    G->arc[4][5]=3;

 

    G->arc[4][6]=6;

    G->arc[4][7]=9;

    G->arc[5][7]=5;

    G->arc[6][7]=2;

    G->arc[6][8]=7;

 

    G->arc[7][8]=4;

 

 

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* Dijkstra算法,求有向網Gv0頂點到其他頂點v的最短路徑P[v]及帶權長度D[v] */

/* P[v]的值爲前驅頂點下標,D[v]表示v0v的最短路徑長度和 */

void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *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.arc[v0][v];/* 將與v0點有連線的頂點加上權值 */

        (*P)[v] = 0;                /* 初始化路徑數組P0 */

    }

 

    (*D)[v0] = 0; /* v0v0路徑爲0 */

    final[v0] = 1; /* v0v0不須要求路徑 */

 

    /* 開始主循環,每次求得v0到某個v頂點的最短路徑 */

    for(v=1; v<G.numVertexes; v++)

    {

        min=INFINITY; /* 當前所知離v0頂點的最近距離 */

        for(w=0; w<G.numVertexes; w++) //遍歷數組D,尋找與v0最近的頂點,已經找到路徑的頂點final=1就不須要再參與了 */

        {

            if(!final[w] && (*D)[w]<min)

            {

                k=w; //到後k存儲這個最近點的下標

                min = (*D)[w]; //找到後min存儲這個最短路徑的權值和

            }

        }

        final[k] = 1; /* 將目前找到的最近的頂點置爲1, 之後就不用再找 */

        for(w=0; w<G.numVertexes; w++) /* 修正當前最短路徑及距離 */

        {

            /* 若是通過v頂點的路徑比如今這條路徑的長度短的話 */

            if(!final[w] && (min+G.arc[k][w]<(*D)[w]))

            { /* 說明找到了更短的路徑,修改D[w]P[w] */

                (*D)[w] = min + G.arc[k][w]; /* 修改當前路徑長度 */

                (*P)[w]=k;

            }

        }

    }

}

 

//主函數

int main(void)

{

    int i,j,v0;

    MGraph G;

    Patharc P;

    ShortPathTable D; /* 求某點到其他各點的最短路徑 */

    v0=0;

    

    CreateMGraph(&G);

    

    ShortestPath_Dijkstra(G, v0, &P, &D);

 

    printf("最短路徑倒序以下:\n");

    for(i=1;i<G.numVertexes;++i)

    {

        printf("v%d - v%d : ",v0,i);

        j=i;

        while(P[j]!=0)

        {

            printf("%d ",P[j]);

            j=P[j];

        }

        printf("\n");

    }

    printf("\n源點到各頂點的最短路徑長度爲:\n");

    for(i=1;i<G.numVertexes;++i)

        printf("v%d - v%d : %d \n",G.vexs[0],G.vexs[i],D[i]);

    return 0;

}

 

  • 最短路徑
----Floyd 算法

//版本1

第1步:初始化矩陣S。 矩陣Sa[i][j]爲頂點i到頂點j的權值;若是ij不相鄰,則a[i][j]=∞。

第2步:以頂點A(1個頂點)爲中介點,若a[i][j] > a[i][0]+a[0][j],則設置a[i][j]=a[i][0]+a[0][j]。 在上面的圖例中, a[1][6](B,G)=∞;而將A做爲中介點時,a[i][0] (B,A)=12a[0][j], (A,G)=14a[i][j] > a[i][0]+a[0][j], 所以BG之間的距離能夠更新爲26

第3: 依次將頂點B,C,D,E,F,G做爲中介點,並更新a[i][j]的大小。

/*

floyd最短路徑:

G --

path -- 路徑。path[i][j]=k表示,"頂點i""頂點j"的最短路徑會通過頂點k

dist -- 長度數組。即,dist[i][j]=sum表示,"頂點i""頂點j"的最短路徑的長度是sum

*/

void floyd(Graph G, int path[][MAX], int dist[][MAX])

{

int i,j,k;

int tmp;

 

// 初始化

for (i = 0; i < G.vexnum; i++)

{

for (j = 0; j < G.vexnum; j++)

{

dist[i][j] = G.matrix[i][j]; // "頂點i""頂點j"的路徑長度爲"ij的權值"

path[i][j] = j; // "頂點i""頂點j"的最短路徑通過了頂點j

}

}

 

// 計算最短路徑

for (k = 0; k < G.vexnum; k++)

{

for (i = 0; i < G.vexnum; i++)

{

for (j = 0; j < G.vexnum; j++)

{

// 若是通過下標爲k頂點路徑比原兩點間路徑更短,則更新dist[i][j]path[i][j]

tmp = (dist[i][k]==INF || dist[k][j]==INF) ? INF : (dist[i][k] + dist[k][j]);

if (dist[i][j] > tmp)

{

// "ij最短路徑"對應的值設爲更小的一個(即通過k)

dist[i][j] = tmp;

// "ij最短路徑"對應的路徑,通過k

path[i][j] = path[i][k];

}

}

}

}

 

// 打印floyd最短路徑的結果

printf("floyd: \n");

for (i = 0; i < G.vexnum; i++)

{

for (j = 0; j < G.vexnum; j++)

printf("%2d ", dist[i][j]);

printf("\n");

}

}

 

//版本2

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

#define MAXEDGE 20

#define MAXVEX 20

#define INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

typedef struct

{

    int vexs[MAXVEX];

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

typedef int Patharc[MAXVEX][MAXVEX];

typedef int ShortPathTable[MAXVEX][MAXVEX];

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

 

    /* printf("請輸入邊數和頂點數:"); */

    G->numEdges=16;

    G->numVertexes=9;

 

    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++)

        {

            if (i==j)

                G->arc[i][j]=0;

            else

                G->arc[i][j] = G->arc[j][i] = INFINITY;

        }

    }

 

    G->arc[0][1]=1;

    G->arc[0][2]=5;

    G->arc[1][2]=3;

    G->arc[1][3]=7;

    G->arc[1][4]=5;

 

    G->arc[2][4]=1;

    G->arc[2][5]=7;

    G->arc[3][4]=2;

    G->arc[3][6]=3;

    G->arc[4][5]=3;

 

    G->arc[4][6]=6;

    G->arc[4][7]=9;

    G->arc[5][7]=5;

    G->arc[6][7]=2;

    G->arc[6][8]=7;

 

    G->arc[7][8]=4;

 

 

    for(i = 0; i < G->numVertexes; i++)

    {

        for(j = i; j < G->numVertexes; j++)

        {

            G->arc[j][i] =G->arc[i][j];

        }

    }

 

}

 

/* Floyd算法,求網圖G中各頂點v到其他頂點w的最短路徑P[v][w]及帶權長度D[v][w]*/

void ShortestPath_Floyd(MGraph G, Patharc *P, ShortPathTable *D)

{

    int v,w,k;

    for(v=0; v<G.numVertexes; ++v) /* 初始化DP */

    {

        for(w=0; w<G.numVertexes; ++w)

        {

            (*D)[v][w]=G.arc[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][k]+(*D)[k][w];/* 將當前兩點間權值設爲更小的一個 */

                    (*P)[v][w]=(*P)[v][k];/* 路徑設置爲通過下標爲k的頂點 */

                }

            }

        }

    }

}

 

//主函數

int main(void)

{

    int v,w,k;

    MGraph G;

    

    Patharc P;

    ShortPathTable D; /* 求某點到其他各點的最短路徑 */

    

    CreateMGraph(&G);

    

    ShortestPath_Floyd(G,&P,&D);

 

    printf("各頂點間最短路徑以下:\n");

    for(v=0; v<G.numVertexes; ++v)

    {

        for(w=v+1; w<G.numVertexes; w++)

        {

            printf("v%d-v%d weight: %d ",v,w,D[v][w]);

            k=P[v][w];                /* 得到第一個路徑頂點下標 */

            printf(" path: %d",v);    /* 打印源點 */

            while(k!=w)                /* 若是路徑頂點下標不是終點 */

            {

                printf(" -> %d",k);    /* 打印路徑頂點 */

                k=P[k][w];            /* 得到下一個路徑頂點下標 */

            }

            printf(" -> %d\n",w);    /* 打印終點 */

        }

        printf("\n");

    }

 

    printf("最短路徑D\n");

    for(v=0; v<G.numVertexes; ++v)

    {

        for(w=0; w<G.numVertexes; ++w)

        {

            printf("%d\t",D[v][w]);

        }

        printf("\n");

    }

    printf("最短路徑P\n");

    for(v=0; v<G.numVertexes; ++v)

    {

        for(w=0; w<G.numVertexes; ++w)

        {

            printf("%d ",P[v][w]);

        }

        printf("\n");

    }

 

    return 0;

}

 

 

拓撲排序

有兩種經常使用的活動網絡( Activity Network):

① AOV(Activity On Vertices)——用頂點表示活動的網絡;

② AOE(Activity On Edges)——用邊表示活動的網絡

 

按照有向圖給出的次序關係,將圖中頂點排成一個線性序列,對於有向圖中沒有限定次序關係的頂點,則能夠人爲加上任意的次序關係。由此所得頂點的線性序列稱之爲拓撲有序序列

 

例如: 對於下列有向圖:

可求得拓撲有序序列:

A B C D A C B D

 

反之,對於下列有向圖:

不能求得它的拓撲有序序列, 由於圖中存在一個迴路 {B, C, D}

對AOV網進行拓撲排序的方法和步驟:

  • 從AOV網中選擇一個沒有前趨的頂點(該頂點的入度爲0)而且輸出它;
  • 從網中刪去該頂點,而且刪去從該頂點發出的所有有向邊;
  • 重複上述兩步,直到剩餘網中再也不存在沒有前趨的頂點爲止。
  • 能夠用鄰接表進行拓撲排序(由於須要刪除頂點,因此咱們選擇鄰接表會更加方便)

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.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 INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

/* 鄰接矩陣結構 */

typedef struct

{

    int vexs[MAXVEX];

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

/* 鄰接表結構 */

typedef struct EdgeNode /* 邊表結點 */

{

    int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */

    int weight;        /* 用於存儲權值,對於非網圖能夠不須要 */

    struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */

}EdgeNode;

 

/* 頂點表結點 */

typedef struct VertexNode

{

    int in;    /* 頂點入度 */

    int data; /* 頂點域,存儲頂點信息 */

    EdgeNode *firstedge;/* 邊表頭指針 */

}VertexNode, AdjList[MAXVEX];

 

typedef struct

{

    AdjList adjList;

    int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */

}graphAdjList,*GraphAdjList;

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

    

    /* printf("請輸入邊數和頂點數:"); */

    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;

 

}

 

/* 利用鄰接矩陣構建鄰接表 */

void CreateALGraph(MGraph G,GraphAdjList *GL)

{

    int i,j;

    EdgeNode *e;

 

    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));

 

    (*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)

            {

                e=(EdgeNode *)malloc(sizeof(EdgeNode));

                e->adjvex=j;                    /* 鄰接序號爲j */

                e->next=(*GL)->adjList[i].firstedge;    /* 將當前頂點上的指向的結點指針賦值給e */

                (*GL)->adjList[i].firstedge=e;        /* 將當前頂點的指針指向e */

                (*GL)->adjList[j].in++;

                

            }

        }

    }

    

}

 

/* 拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回1,如有迴路返回0*/

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(0 == GL->adjList[i].in) /* 將入度爲0的頂點入棧 */

            stack[++top]=i;

    while(top!=0)

    {

        gettop=stack[top--];

        printf("%d -> ",GL->adjList[gettop].data);

        count++; /* 輸出i號頂點,並計數 */

        for(e = GL->adjList[gettop].firstedge; e; e = e->next)

        {

            k=e->adjvex;

            if( !(--GL->adjList[k].in) ) /* i號頂點的鄰接點的入度減1,若是減1入度爲0,則入棧, 以便下次循環輸出 */

                stack[++top]=k;

        }

    }

    printf("\n");

    if(count < GL->numVertexes) //若是count小於頂點數, 說明存在環

        return ERROR;

    else

        return OK;

}

 

//主函數

int main(void)

{

    MGraph G;

    GraphAdjList GL;

    int result;

    CreateMGraph(&G);

    CreateALGraph(G,&GL);

    result=TopologicalSort(GL);

    printf("result:%d",result);

 

    return 0;

}

 

關鍵路徑

路徑長度: 路徑上各個活動所持續的時間之和

關鍵路徑: 從源點到匯點具備最大長度的路徑叫關鍵路徑,在關鍵路徑上的活動叫關鍵活動。

//起始部分

#include "stdio.h"

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

 

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

 

#define MAXEDGE 30

#define MAXVEX 30

#define INFINITY 65535

 

typedef int Status;    /* Status是函數的類型,其值是函數結果狀態代碼,如OK*/

 

int *etv,*ltv; /* 事件最先發生時間和最遲發生時間數組,全局變量 */

int *stack2; /* 用於存儲拓撲序列的棧 */

int top2;     /* 用於stack2的指針 */

 

/* 鄰接矩陣結構 */

typedef struct

{

    int vexs[MAXVEX];

    int arc[MAXVEX][MAXVEX];

    int numVertexes, numEdges;

}MGraph;

 

/* 鄰接表結構 */

typedef struct EdgeNode /* 邊表結點 */

{

    int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */

    int weight;        /* 用於存儲權值,對於非網圖能夠不須要 */

    struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */

}EdgeNode;

 

/* 頂點表結點 */

typedef struct VertexNode

{

    int in;    /* 頂點入度 */

    int data; /* 頂點域,存儲頂點信息 */

    EdgeNode *firstedge;/* 邊表頭指針 */

}VertexNode, AdjList[MAXVEX];

 

typedef struct

{

    AdjList adjList;

    int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */

}graphAdjList,*GraphAdjList;

 

/* 構件圖 */

void CreateMGraph(MGraph *G)

{

    int i, j;

    /* printf("請輸入邊數和頂點數:"); */

    G->numEdges=13;

    G->numVertexes=10;

 

    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++)

        {

            if (i==j)

                G->arc[i][j]=0;

            else

                G->arc[i][j]=INFINITY;

        }

    }

 

    G->arc[0][1]=3;

    G->arc[0][2]=4;

    G->arc[1][3]=5;

    G->arc[1][4]=6;

    G->arc[2][3]=8;

    G->arc[2][5]=7;

    G->arc[3][4]=3;

    G->arc[4][6]=9;

    G->arc[4][7]=4;

    G->arc[5][7]=6;

    G->arc[6][9]=2;

    G->arc[7][8]=5;

    G->arc[8][9]=3;

 

}

 

/* 利用鄰接矩陣構建鄰接表 */

void CreateALGraph(MGraph G,GraphAdjList *GL)

{

    int i,j;

    EdgeNode *e;

 

    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));

 

    (*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]!=0 && G.arc[i][j]<INFINITY)

            {

                e=(EdgeNode *)malloc(sizeof(EdgeNode));

                e->adjvex=j;                    /* 鄰接序號爲j */

                e->weight=G.arc[i][j];

                e->next=(*GL)->adjList[i].firstedge;    /* 將當前頂點上的指向的結點指針賦值給e */

                (*GL)->adjList[i].firstedge=e;        /* 將當前頂點的指針指向e */

                (*GL)->adjList[j].in++;

                

            }

        }

    }

    

}

 

/* 拓撲排序 */

Status TopologicalSort(GraphAdjList GL)

{ /* GL無迴路,則輸出拓撲排序序列並返回1,如有迴路返回0*/

    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(0 == GL->adjList[i].in) /* 將入度爲0的頂點入棧 */

            stack[++top]=i;

 

    top2=0;

    etv=(int *)malloc(GL->numVertexes * sizeof(int) ); /* 事件最先發生時間數組 */

    for(i=0; i<GL->numVertexes; i++)

        etv[i]=0; /* 初始化 */

    stack2=(int *)malloc(GL->numVertexes * sizeof(int) );/* 初始化拓撲序列棧 */

 

    printf("TopologicalSort:\t");

    while(top!=0)

    {

        gettop=stack[top--];

        printf("%d -> ",GL->adjList[gettop].data);

        count++; /* 輸出i號頂點,並計數 */ 

 

        stack2[++top2]=gettop; /* 將彈出的頂點序號壓入拓撲序列的棧 */

 

        for(e = GL->adjList[gettop].firstedge; e; e = e->next)

        {

            k=e->adjvex;

            if( !(--GL->adjList[k].in) ) /* i號頂點的鄰接點的入度減1,若是減1後爲0,則入棧 */

                stack[++top]=k;

 

            if((etv[gettop] + e->weight)>etv[k]) /* 求各頂點事件的最先發生時間etv*/

                etv[k] = etv[gettop] + e->weight;

        }

    }

    printf("\n");

    if(count < GL->numVertexes)

        return ERROR;

    else

        return OK;

}

 

/* 求關鍵路徑,GL爲有向網,輸出G的各項關鍵活動 */

void CriticalPath(GraphAdjList GL)

{

    EdgeNode *e;

    int i,gettop,k,j;

    int ete,lte; /* 聲明活動最先發生時間和最遲發生時間變量 */

    TopologicalSort(GL); /* 求拓撲序列,計算數組etvstack2的值 */ 

    ltv=(int *)malloc(GL->numVertexes*sizeof(int));/* 事件最先發生時間數組 */

    for(i=0; i<GL->numVertexes; i++)

        ltv[i]=etv[GL->numVertexes-1]; /* 初始化 */

    

    printf("etv:\t");

    for(i=0; i<GL->numVertexes; i++)

        printf("%d -> ",etv[i]);

    printf("\n");

 

    while(top2!=0) /* 出棧是求ltv */

    {

        gettop=stack2[top2--];

        for(e = GL->adjList[gettop].firstedge; e; e = e->next) /* 求各頂點事件的最遲發生時間ltv值 */

        {

            k=e->adjvex;

            if(ltv[k] - e->weight < ltv[gettop])

                ltv[gettop] = ltv[k] - e->weight;

        }

    }

    

    printf("ltv:\t");

    for(i=0; i<GL->numVertexes; i++)

        printf("%d -> ",ltv[i]);

    printf("\n");

 

    for(j=0; j<GL->numVertexes; j++) /* 求ete,lte和關鍵活動 */

    {

        for(e = GL->adjList[j].firstedge; e; e = e->next)

        {

            k=e->adjvex;

            ete = etv[j]; /* 活動最先發生時間 */

            lte = ltv[k] - e->weight; /* 活動最遲發生時間 */

            if(ete == lte) /* 二者相等即在關鍵路徑上 */

                printf("<v%d - v%d> length: %d \n",GL->adjList[j].data,GL->adjList[k].data,e->weight);

        }

    }

}

 

//主函數

int main(void)

{

    MGraph G;

    GraphAdjList GL;

    CreateMGraph(&G);

    CreateALGraph(G,&GL);

    CriticalPath(GL);

    return 0;

}

 

相關文章
相關標籤/搜索