1. 程式人生 > >[Algorithm]圖

[Algorithm]圖

一.圖的演算法


 

鄰接矩陣表示的資料結構

 1 #define INFINITY INT_MAX // 無窮大
 2 #define MAX_VERTEX_NUM 20 // 限制頂點最大數值為20個
 3 #define MAX_ARC_NUM  MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1) // 由n個頂點,最多可以確定n(n-2)/2條直線,有向圖為2倍
 4 #define MAX_INFO 20 // 使用者輸入的弧資訊,最多20個字元
 5 
 6 /*陣列表示法*/
 7 typedef int        VRType;
 8 typedef char    InfoType;
 9 typedef char    VertexType[5];
10 typedef enum    {DG, DN, UDG, UDN} GraphKind; 
11 
12 typedef struct ArcCell { 13  VRType adj; 14 InfoType *info; 15 }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; 16 17 typedef struct { 18  VertexType vexs[MAX_VERTEX_NUM]; 19  AdjMatrix arcs; 20 int vexnum, arcnum; 21 }MGraph;

鄰接表表示的資料結構

 1 /*鄰接表表示法*/
 2 typedef struct ArcNode
 3 {
 4     int                adjvex;
 5     int                w; // 儲存權值,書中的程式沒有表示權值的資料成員(書中說用info來儲存權值,但是上面的程式又是單獨用的adj存權值,為了一致性,info還是用來儲存其他資訊算了)
 6     struct ArcNode    *nextarc;
 7     InfoType *info; // 用來儲存權值以外的有關弧的資訊
 8 }ArcNode; 9 10 typedef struct VNode 11 { 12  VertexType data; 13 ArcNode *firstarc; 14 }VNode, AdjList[MAX_VERTEX_NUM]; 15 16 typedef struct 17 { 18  AdjList vertices; 19 int vexnum, arcnum; 20 int kind; 21 }ALGraph;

1.寫出從圖的鄰接表表示轉換成鄰接矩陣表示的演算法

1 void Convert( ALGraph G, int arcs[][10] )
2 {
3     for ( int v = 0; v < G.vexnum; v++ )
4         for ( ArcNode* p = G.vertices[v].firstarc; p; p->nextarc )
5             arcs[v][p->adjvex] = 1; 6 }

二.圖的遍歷


 

說明: 以下圖的演算法既可以使用鄰接矩陣的方式也可以使用鄰接表儲存的方式,因此每種演算法都可以換成另一種儲存形式,只需要把MGraph(鄰接矩陣儲存)換成ALGraph(鄰接表儲存)即可

基本資料結構:

鄰接矩陣下,通用找鄰接的函式:

 1 // 第一個鄰居
 2 int FirstNeighbor( MGraph G, int v)
 3 { 
 4     for ( int i = 0; i < G.vexnum; i++ )
 5         if ( G.arcs[v][i] == 1 )
 6             return i; 7 return -1; 8 } 9 // 當前的下一個鄰居 10 int NextNeighbor( MGraph G, int v, int w ) 11 { 12 for (int i = w+1; i < G.vexnum; i++ ) 13 if ( G.arcs[v][i] == 1 ) 14 return i; 15 return -1; 16 }

鄰接表下,通用找鄰接的函式:

 1 /*全域性變數*/
 2 bool Visited[MAX_VERTEX_NUM]; // 記錄每個頂點是否被訪問過
 3 
 4 // 找到第一個v相鄰的頂點,返回它的下標
 5 int FirstAdjVex(ALGraph &AL, int v)
 6 {
 7     ArcNode *p = NULL;
 8     p = AL.vertices[v].firstarc;
 9     if (p == NULL) 10 return -1; 11 else 12 return p->adjvex; 13 } 14 15 // 找到下一個與v相鄰的頂點,返回它的下標 16 int NextAdjVex(ALGraph &AL, int v, int w) 17 { 18 ArcNode *p = NULL; 19 p = AL.vertices[v].firstarc; 20 while (p->adjvex != w) // 找到下標為w的結點 21 p = p->nextarc; 22 p = p->nextarc; // 指標指向下標為w的結點的後面一個結點 23 if (p == NULL) 24 return -1; 25 else 26 return p->adjvex; 27 }

1.廣度優先搜尋(Breadth-First-Search, BFS)

法一:採用鄰接矩陣

 1 bool visited[MAX_VERTEX_NUM] = { false };
 2 void BFS( MGraph G, int v );
 3 
 4 void BFSTraverse( MGraph G )
 5 {
 6     int Q[MAXSIZE]; 7 int front = -1, rear = -1; 8 for (int v = 0; v < G.vexnum; v++ ) 9 if ( !visited[v] ) 10  BFS( G, v ); 11 } 12 13 void BFS( MGraph G, int v ) 14 { 15 int Q[MAXSIZE]; 16 int front = -1, rear = -1; 17 // BFS頂點三連 18  visit( v ); 19 visited[v] = true; 20 Q[++rear] = v; 21 22 while ( front != rear ) 23  { 24 v = Q[++front]; 25 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 26  { 27 if (!visited[w]) 28  { 29 // BFS頂點三連 30  visit( w ); 31 visited[w] = true; 32 Q[++rear] = w; 33  } 34  } 35  } 36 }

法二:採用鄰接表

 1 bool visited[MAX_VERTEX_NUM] = { false };
 2 void BFS( ALGraph G, int v );
 3 
 4 void BFSTraverse( ALGraph G )
 5 {
 6     int Q[MAXSIZE]; 7 int front = -1, rear = -1; 8 for (int v = 0; v < G.vexnum; v++ ) 9 if ( !visited[v] ) 10  BFS( G, v ); 11 } 12 13 void BFS( ALGraph G, int v ) 14 { 15 int Q[MAXSIZE]; 16 int front = -1, rear = -1; 17 // BFS頂點三連 18  visit( v ); 19 visited[v] = true; 20 Q[++rear] = v; 21 22 while ( front != rear ) 23  { 24 v = Q[++front]; 25 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 26  { 27 if (!visited[w]) 28  { 29 // BFS頂點三連 30  visit( w ); 31 visited[w] = true; 32 Q[++rear] = w; 33  } 34  } 35  } 36 }

2.BFS演算法求解單源最短路徑問題

 1 bool visited[MAXSIZE] = { false };
 2 unsigned int d[MAXSIZE] = { INFINITE };
 3 void BFS_MIN_Distance( ALGraph G, int u )
 4 {
 5  BiTree Q[MAXSIZE]; 6 int front = -1, rear = -1, v, w; 7 // BFS路徑三連 8 d[u] = 0; 9 visited[u] = true; 10 Q[++rear] = u; 11 12 while ( front != rear ) 13  { 14 v = Q[++front]; 15 for ( w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 16  { 17 if (!visited[w]) 18  { 19 // BFS路徑三連 20 d[w] = d[v] + 1; 21 visited[w] = true; 22 Q[++rear] = w; 23  } 24  } 25  } 26 }

3.深度優先搜尋(Depth-First-Search, DFS)

 1 bool visited[MAX_VERTEX_NUM] = { false };
 2 void DFS( ALGraph &G, int v );
 3 
 4 void DFSTraverse( ALGraph &G )
 5 {
 6     for ( int v = 0; v < G.vexnum; v++ ) 7 if ( !visited[v] ) 8  DFS( G, v ); 9 } 10 11 void DFS( ALGraph &G, int v ) 12 { 13  visit( v ); 14 visited[v] = true; 15 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 16 if ( !visited[w] ) 17  DFS( G, w ); 18 }

4.設計一個演算法,判斷一個無向圖G是否為一棵樹

 1 int visited[MAXSIZE] = { 0 };
 2 void DFS( MGraph G, int v, int& Vnum, int& TD );
 3 
 4 bool IsTree( MGraph G )
 5 {
 6     int Vnum = 0, TD = 0;    // TD=total degree總度數
 7     DFS( G, 0, Vnum, TD );    // 從第一個頂點開始遍歷
 8     if ( Vnum == G.vexnum&&TD == 2 * ( G.vexnum - 1 ) ) 9 return true; 10 return false; 11 } 12 13 void DFS( MGraph G, int v, int& Vnum, int& TD ) 14 { 15 visited[v] = true; Vnum++; 16 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 17 if ( !visited[w] ) 18  DFS( G, w, Vnum, TD ); 19 }

5.寫出圖的深度優先搜尋DFS演算法的非遞迴演算法

 1 bool visited[MAXSIZE] = { false };
 2 void DFS_NON_RC( MGraph G, int v )
 3 {
 4     int S[MAXSIZE];
 5     int top = -1; 6 for ( int i = 0; i < G.vexnum; i++ ) 7 visited[i] = false; 8 // 頂點二連 9 visited[v] = true; 10 S[++top] = v; 11 12 while ( top != -1 ) 13  { 14 v = S[top--]; visit( v ); 15 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 16 if ( !visited[w] ) 17  { 18 // 頂點二連 19 visited[w] = true; 20 S[++top] = w; 21  } 22  } 23 }

6.分別採用基於廣度優先遍歷和深度優先遍歷演算法判別以鄰接表或鄰接矩陣儲存的有向圖中是否存在由頂點v到頂點u的路徑($v\neq u$)

// 採用BFS的方法
bool visited[MAXSIZE] = { false };
bool Exist_Path_BFS( MGraph G, int v, int u )
{
    int Q[MAXSIZE];
    int front = -1, rear = -1;
    visited[v] = true; Q[++rear] = v; while ( front != rear ) { v = Q[++front]; for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) { if (!visited[w]) { if ( w == u ) return true; visited[w] = true; Q[++rear] = w; } } } return false; }
 1 // 採用DFS的方法
 2 bool visited[MAXSIZE] = { false };
 3 bool Exist_Path_DFS( MGraph G, int v, int u )
 4 {
 5     if ( v == u ) return true;
 6     visited[v] = true; 7 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 8  { 9 if ( !visited[w] ) 10  { 11 if ( Exist_Path_DFS( G, w, u ) ) return true; 12  } 13  } 14 return false; 15 }

7.拓撲排序:判斷並輸出有向圖的拓撲序列

 1 bool Topological( MGraph G, int indegree[] )
 2 {
 3     int S[MAXSIZE];
 4     int top = -1, Vnum = 0, v = 0;
 5     for ( v = 0; v < G.vexnum; v++ ) 6  { 7 if ( indegree[v] == 0 ) 8  { 9  visit( v ); 10 Vnum++; 11 S[++top] = v; 12  } 13  } 14 while ( top != -1 ) 15  { 16 v = S[top--]; 17 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 18  { 19 indegree[w]--; 20 if ( indegree[w] == 0 ) 21  { 22  visit( w ); 23 Vnum++; 24 S[++top] = w; 25  } 26  } 27  } 28 if ( Vnum == G.vexnum ) 29 return true; 30 return false; 31 }

2.拓撲排序(DFS):有向無環圖的拓撲排序

 1 bool visited[MAXSIZE] = { false };
 2 int time = 0, finishTime[MAXSIZE] = { 0 };
 3 void DFS( MGraph G, int v );
 4 
 5 void Topological_DFS( MGraph G )
 6 { 7 for ( int v = 0; v < G.vexnum; v++ ) 8 if ( !visited[v] ) 9  DFS( G, v ); 10 for ( int t = time - 1; t >= 0; t-- ) 11  visit( finishTime[t] ); 12 } 13 14 void DFS( MGraph G, int v ) 15 { 16 visited[v] = true; 17 for ( int w = FirstNeighbor( G, v ); w >= 0; w = NextNeighbor( G, v, w ) ) 18 if ( !visited[w] ) 19  DFS( G, w ); 20 finishTime[time++] = v; 21 }