数据结构—无向图创建邻接矩阵、深度优先遍历和广度优先遍历(C语言版)

摘自:数据结构—无向图创建邻接矩阵、深度优先遍历和广度优先遍历(C语言版)
作者:正弦定理
发布时间:2020-12-19 17:25:49
网址:https://blog.csdn.net/zhuguanlin121/article/details/118436142

一、概念解析:

(1)无向图:

假设图G由两个集合V和E组成,记为G={V , E}。其中V是顶点的有限集合,E是连接V中两个不同顶点的边的有限集合。如果E中的顶点对是有序的,即E中的每条边都是有方向的,则称G是有向图。如果顶点对是无序的,则称G是无向图

在这里插入图片描述

在这里插入图片描述

(2)邻接矩阵:

邻接矩阵主要由:二维数组 实现

如图
在这里插入图片描述

转换成邻接矩阵为:

在这里插入图片描述

二、创建邻接矩阵:

基本每一步都有注释,详细观看,建议画图理解

代码如下:

#define MAXSIZE 100 
//	邻接矩阵 
typedef struct Matrix{
	
	int V_Data;		//	顶点数据域 
	int E_Data;		//	边数数据域
	
	int Node[MAXSIZE];	//	存放顶点数据,也就是顶点表 
	int Weight[MAXSIZE][MAXSIZE]; 	//	存放权重,为矩阵中两点有边的标记符号 
	
}MaTrix,*MATRIX;

//	邻接矩阵数据结构体 
typedef struct Edge{
	
	int v1;		//	用来存放第一个顶点 
	int v2;		//	用来存放第二个顶点
	int weight;	//	用来存放两点之间的标记符,即为权 
}*EDGE;


//******************** 邻接矩阵*******************//
//	邻接矩阵、顶点和边初始化 
void Init_Matrix(MATRIX S,int Vertex)
{	
	S->E_Data = 0;			//	初始化为0条边 
	S->V_Data = Vertex;		//	初始化顶点数 
	
	int i,j;
	for(i=0;i<Vertex;i++)
	{
		for(j=0;j<Vertex;j++)
		{
			S->Weight[i][j] = 0;
		}
	} 
}

//	开始插入边的权重,即为两个顶点之间边的标记符
void InSerData(MATRIX S,EDGE E)
{
	//	将输入的顶点v1、v2之间的边,用权作为标记,在矩阵中表示
	//	这里是无向图,所以边没有方向,需要做标记两次(为v1-v2和v2-v1) 
	S->Weight[E->v1][E->v2] = E->weight;	 
	S->Weight[E->v2][E->v1] = E->weight; 
} 

//	开始插入数据 
void InSerEdge_Data(MATRIX S,int edge,int V)
{
	int i,j;
	
	if(edge>0)	//	边数大于0的时候才插入数据 
	{
		printf("请输入顶点和权重(空格分隔!)\n");
		for(i=0;i<edge;i++)
		{		
			EDGE E;				//分配内存,接受顶点v1,v2和权重(标记符)	
			E = (EDGE)malloc(sizeof(struct Edge));	
			
			scanf("%d %d %d",&(E->v1),&(E->v2),&(E->weight));
			
			if(E->v1 ==E->v2)
			{
				printf("无向图邻接矩阵对角线为0,输入错误,结束运行\n");
				exit(-1); 
			}
			
			InSerData(S,E);
		}	
		printf("请输入要定义的顶点,填入顶点表中: \n");
		for(j=0;j<V;j++)
		{
			scanf("%d",&(S->Node[j]));
		}
		
	
	}else{	
		printf("输入的边数错误"); 
	} 
		
} 

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

三、深度遍历、广度遍历

(1)深度遍历概念:

在这里插入图片描述

在这里插入图片描述

定义的结构体、数组可看上面代码

深度遍历代码解析:

//*****************	深度优先遍历算法—邻接矩阵 *****************//
void DFS_Begin(MATRIX P,int k,int V)
{
	int i;
	flag[k] = 1;	//标记当前顶点,表示已经遍历过

	printf("%d ",P->Node[k]);	//	输出当前顶点 
	
	for(i=0;i<V;i++)
	{
		if(!flag[i] && P->Weight[k][i] != 0)//	如果当前顶点的邻近点存在,且没有遍历过 
		{									//	则继续递归遍历 
		
			DFS_Begin(P,i,V);		//	递归遍历当前顶点的邻近点 
		}	
	} 
}

void Init_DFSMatrix(MATRIX P,int V)
{
	int i;
	//	初始化标记符数组,全为0 
	for(i=0;i<V;i++)
	{
		flag[i] = 0;
	}
	
	for(i=0;i<V;i++)	//	每个顶点都要检查是否遍历到 
	{
		if(!flag[i])	//	排除遇到已经遍历的顶点
			DFS_Begin(P,i,V);		//	开始深度遍历	
	} 
	putchar('\n');
	
	
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

(2)广度遍历概念:

在这里插入图片描述

这里使用到了链队列(也可以使用数组队列,看个人想法),可以看我之前的博文有讲:

//******************** 队列 *****************//
typedef struct Queue{
	
	int data[MAXSIZE];	//	队列大小 
	int head;	//	队头 
	int wei;	//	队尾 
	
}Queue; 

//*****************	队列 *************************************//
//	队列初始化 
void InitQueue(Queue *q)
{
	q->head= 0;		//	初始化队头、队尾 
	q->wei = 0;
} 

//	判断队列是否为空
int EmptyQueue(Queue *q)
{
	if(q->head == q->wei)
		return 1;
	else{
		return 0;
	}		
} 

//	入队
void PushQueue(Queue *q,int t)
{
	if((q->wei+1)%MAXSIZE == q->head)	//	说明队列已经满了
		return;
	else{	
		q->data[q->wei] = t;	
		q->wei = (q->wei +1)%MAXSIZE;	//	队尾后移 
	}
		 
} 

//	出队
void PopQueue(Queue *q,int *x)
{
	if(q->wei == q->head)	//	出队完毕 
		return;	
	else{	 	
		*x = q->data[q->head];
		q->head = (q->head + 1)%MAXSIZE; //	队头后移	
	}	
	
} 
 
//***************** 广度优先搜索算法—邻接矩阵 ****************//
void Init_Bfs(MATRIX S,int V)
{
	int i,j;
	int k;
	 
	Queue Q;
	
	for(i=0;i<V;i++)
	{
		Vist[i] = 0;	//	初始化标记符 
	}
	
	InitQueue(&Q);	//	队列初始化 
	
	for (i = 0; i < V; i++)
	{
		if (!Vist[i])	//	判断以这个顶点为基准,有连接的其他顶点 
		{
			Vist[i] = 1;	//	标记遍历的这个顶点 
			printf("%d ", S->Node[i]);
		
			PushQueue(&Q, i);	//	入队 

			while (!EmptyQueue(&Q))	//	队列中还有数据,说明这个顶点连接的其他顶点还没有遍历完 
			{
				PopQueue(&Q,&i);	//	出队 
				
				for (j = 0; j < V; j++)
				{
					//	以这个顶点为基准,遍历其他连接的顶点 
					if (!Vist[j] && S->Weight[i][j] != 0)
					{
						Vist[j] = 1;	//	与之连接的顶点作上标记,便于后序顶点跳过相同的遍历 
						printf("%d ", S->Node[j]);//	输出与之相邻连接的顶点 
						PushQueue(&Q, j);	//	让与之连接的顶点其位置入队 
					}
				}
			}
		}
	}
} 


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

四、实例展示

注意:这里存入数据时,坐标点以原点(0,0)为起点开始!

以这个图为样例展示:

在这里插入图片描述

全部代码:

#include<stdio.h>
#include<stdlib.h>

#define MAXSIZE 100 

//	深度遍历标记符
int flag[MAXSIZE]; 		//	邻接矩阵 

//	广度优先遍历标记符 
int Vist[MAXSIZE]; 		//	邻接矩阵

//******************** 队列 *****************//
typedef struct Queue{
	
	int data[MAXSIZE];	//	队列大小 
	int head;	//	队头 
	int wei;	//	队尾 
	
}Queue; 

//	邻接矩阵 
typedef struct Matrix{
	
	int V_Data;		//	顶点数据域 
	int E_Data;		//	边数数据域
	
	int Node[MAXSIZE];	//	存放顶点数据,也就是顶点表 
	int Weight[MAXSIZE][MAXSIZE]; 	//	存放权重,为矩阵中两点有边的标记符号 
	
}MaTrix,*MATRIX;

//	邻接矩阵数据结构体 
typedef struct Edge{
	
	int v1;		//	用来存放第一个顶点 
	int v2;		//	用来存放第二个顶点
	int weight;	//	用来存放两点之间的标记符,即为权 
}*EDGE;


//******************** 邻接矩阵*******************//
//	邻接矩阵、顶点和边初始化 
void Init_Matrix(MATRIX S,int Vertex)
{	
	S->E_Data = 0;			//	初始化为0条边 
	S->V_Data = Vertex;		//	初始化顶点数 
	
	int i,j;
	for(i=0;i<Vertex;i++)
	{
		for(j=0;j<Vertex;j++)
		{
			S->Weight[i][j] = 0;
		}
	} 
}

//	开始插入边的权重,即为两个顶点之间边的标记符
void InSerData(MATRIX S,EDGE E)
{
	//	将输入的顶点v1、v2之间的边,用权作为标记,在矩阵中表示
	//	这里是无向图,所以边没有方向,需要做标记两次(为v1-v2和v2-v1) 
	S->Weight[E->v1][E->v2] = E->weight;
	 
	S->Weight[E->v2][E->v1] = E->weight; 
} 

//*****************	深度优先遍历算法—邻接矩阵 *****************//
void DFS_Begin(MATRIX P,int k,int V)
{
	int i;
	flag[k] = 1;	//标记当前顶点,表示已经遍历过

	printf("%d ",P->Node[k]);	//	输出当前顶点 
	
	for(i=0;i<V;i++)
	{
		if(!flag[i] && P->Weight[k][i] != 0)//	如果当前顶点的邻近点存在,且没有遍历过 
		{									//	则继续递归遍历 
		
			DFS_Begin(P,i,V);		//	递归遍历当前顶点的邻近点 
		}	
	} 
}

void Init_DFSMatrix(MATRIX P,int V)
{
	int i;
	//	初始化标记符数组,全为0 
	for(i=0;i<V;i++)
	{
		flag[i] = 0;
	}
	
	for(i=0;i<V;i++)	//	每个顶点都要检查是否遍历到 
	{
		if(!flag[i])	//	排除遇到已经遍历的顶点
			DFS_Begin(P,i,V);		//	开始深度遍历	
	} 
	putchar('\n');
	
	
}

//*****************	队列 *************************************//
//	队列初始化 
void InitQueue(Queue *q)
{
	q->head= 0;		//	初始化队头、队尾 
	q->wei = 0;
} 

//	判断队列是否为空
int EmptyQueue(Queue *q)
{
	if(q->head == q->wei)
		return 1;
	else{
		return 0;
	}		
} 

//	入队
void PushQueue(Queue *q,int t)
{
	if((q->wei+1)%MAXSIZE == q->head)	//	说明队列已经满了
		return;
	else{	
		q->data[q->wei] = t;	
		q->wei = (q->wei +1)%MAXSIZE;	//	队尾后移 
	}
		 
} 

//	出队
void PopQueue(Queue *q,int *x)
{
	if(q->wei == q->head)	//	出队完毕 
		return;	
	else{
		 	
		*x = q->data[q->head];
		q->head = (q->head + 1)%MAXSIZE; //	队头后移
	
	}	
	
	
} 
 
//***************** 广度优先搜索算法—邻接矩阵 ****************//
void Init_Bfs(MATRIX S,int V)
{
	int i,j;
	int k;
	 
	Queue Q;
	
	for(i=0;i<V;i++)
	{
		Vist[i] = 0;	//	初始化标记符 
	}
	
	InitQueue(&Q);	//	队列初始化 
	
	for (i = 0; i < V; i++)
	{
		if (!Vist[i])	//	判断以这个顶点为基准,有连接的其他顶点 
		{
			Vist[i] = 1;	//	标记遍历的这个顶点 
			printf("%d ", S->Node[i]);
		
			PushQueue(&Q, i);	//	入队 

			while (!EmptyQueue(&Q))	//	队列中还有数据,说明这个顶点连接的其他顶点还没有遍历完 
			{
				PopQueue(&Q,&i);	//	出队 
				
				for (j = 0; j < V; j++)
				{
					//	以这个顶点为基准,遍历其他连接的顶点 
					if (!Vist[j] && S->Weight[i][j] != 0)
					{
						Vist[j] = 1;	//	与之连接的顶点作上标记,便于后序顶点跳过相同的遍历 
						printf("%d ", S->Node[j]);//	输出与之相邻连接的顶点 
						PushQueue(&Q, j);	//	让与之连接的顶点其位置入队 
					}
				}
			}
		}
	}
} 


//	初始化顶点个数 
int Init_Vertex()
{
	int Vertex;
				 
	printf("请输入顶点个数: ");
	scanf("%d",&Vertex);
	return Vertex;
}

//	初始化边的数量 
int Init_Edge()
{
	int edge;
	printf("请输入边的数量: ");
	scanf("%d",&edge);
	return edge;
		
} 

//	开始插入数据 
void InSerEdge_Data(MATRIX S,int edge,int V)
{
	int i,j;
	
	if(edge>0)	//	边数大于0的时候才插入数据 
	{
		printf("请输入顶点和权重(空格分隔!)\n");
		for(i=0;i<edge;i++)
		{		
			EDGE E;				//分配内存,接受顶点v1,v2和权重(标记符)	
			E = (EDGE)malloc(sizeof(struct Edge));	
			
			scanf("%d %d %d",&(E->v1),&(E->v2),&(E->weight));
			
			if(E->v1 ==E->v2)
			{
				printf("无向图邻接矩阵对角线为0,输入错误,结束运行\n");
				exit(-1); 
			}
			
			InSerData(S,E);
		}	
		printf("请输入要定义的顶点,填入顶点表中: \n");
		for(j=0;j<V;j++)
		{
			scanf("%d",&(S->Node[j]));
		}
		
	
	}else{
		
		printf("输入的边数错误"); 
	} 
		
} 
//	打印无向图邻接矩阵 
void Show_Matrix(MATRIX p,int Vertex)
{
	int i,j;
	for(i=0;i<Vertex;i++)
	{
		for(j=0;j<Vertex;j++)
		{
			printf("%4d",p->Weight[i][j]);	//	打印邻接矩阵 
		}	
			putchar('\n');	//	换行 
	}
}

int main()
{
	
	int val;
	int Vertex;
	int edge;
	
	MATRIX p;		//	邻接矩阵头节点指针

	//	创建无向图邻接矩阵 					
	Vertex = Init_Vertex();
	edge = Init_Edge();
				
	p = (MATRIX)malloc(sizeof(MaTrix));		//分配内存空间 
				
	p->V_Data = Vertex;		//	记录顶点个数 
	p->E_Data = edge;		//	记录边的个数 
				
	Init_Matrix(p,Vertex);	//	初始化邻接矩阵 
				
	InSerEdge_Data(p,edge,Vertex);	//	插入数据 
				
	//	打印无向图的邻接矩阵 
	printf("无向图邻接矩阵如下:");	
	printf("\n----------------------------------\n\n");
	Show_Matrix(p,Vertex);
	printf("\n----------------------------------\n");
	
	//	深度优先遍历—邻接矩阵	
	printf("深度遍历—邻接矩阵结果为:\n");
	Init_DFSMatrix(p,Vertex);
	
			
	//	广度优先遍历—邻接矩阵
	printf("广度优先遍历—邻接矩阵结果为: \n");
	Init_Bfs(p,Vertex);
				
	return 0;	
} 


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303

结果图:
在这里插入图片描述

在这里插入图片描述

  • 23
    点赞
  • 173
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值