一.图的算法
邻接矩阵表示的数据结构
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 }