图用邻接矩阵实现,深度优先遍历和广度优先遍历

邻接矩阵的结构体

#define MAXVertexNum 20//顶点数目最大值
typedef char Vertextype;//顶点的数据类型
typedef int Edgetype;//带权图中边上权值的数据类型
typedef struct
{
	Vertextype Vertex[MAXVertexNum];//顶点表
	Edgetype Edge[MAXVertexNum][MAXVertexNum];//邻接矩阵,边表
	int vernum, arcnum; //图的顶点数和弧数
}MGraph;

邻接矩阵图的建立

        图的建立有多种实现方式,我这里是从键盘输入顶点数,边条数,并从键盘输入边的关系

图是带有权值的,并且把环的权值赋值为0,两个顶点没有边权值为32767。

void CreateMGraph(MGraph *G)//图的建立
{
	char cls;//用于清除缓冲区的回车
	int i, j, w, k;
	printf("请输入顶点的数量\n");
	scanf_s("%d", &(G->vernum));
	cls = getchar();//清除缓冲区回车
	printf("请输入边的条数\n");
	scanf_s("%d", &(G->arcnum));
	cls = getchar();
	printf("请输入顶点信息\n");
	for (i = 0; i < G->vernum; i++)//给存储顶点数组赋值
	{
		scanf_s("%c", &(G->Vertex[i]), 1);
	}
	cls = getchar();//清除缓冲区回车
	for (i = 0; i < G->vernum; i++)//初始化边的关系
	{
		for (j = 0; j < G->vernum; j++)
		{
			if (j == i)
			{
				G->Edge[i][j] = 0;//这里我把指向自己的边权值赋值为0
			}
			else
			{
				G->Edge[i][j] = 32767;//指向别人的边赋值为32767
			}
		}
	}
	for (k = 0; k < G->arcnum; k++)//给边赋值
	{
		printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
		scanf_s("%d%d%d", &i, &j, &w);
		cls = getchar();
		G->Edge[i - 1][j - 1] = w;
		G->Edge[j - 1][i - 1] = w;//无向图具有对称性
	}
}

查找顶点v,并且返回v的相关信息

        通过循环去找顶点,如果找到了打印出顶点的位置(节点数组的第几个),并找出与之相连的边,找到了打印出与哪条边相关联,如果想知道权值也可加入

printf("权值为:%d\n",G->Edge[i][j]);

void GetVex(MGraph* G, Vertextype v)//找到顶点v并返回v的相关信息
{
	int i, j;
	for (i = 0; i < G->vernum; i++)
	{
		if (v == G->Vertex[i])
		{
			break;//v存在的话就退出
		}
	}
	if (i == G->vernum)//判断v是否存在
	{
		printf("没找到:%c\n", v);
		return;
	}
	else
	{
		printf("顶点在第:%d个位置\n", i + 1);//打印v在的位置
		for (j = 0; j < G->vernum; j++)
		{
			if (G->Edge[i][j] != 0 && G->Edge[j][i] != 32767)//打印和v存在边关系的vertex[j]
			{
				printf("%c和%c存在边\n", G->Vertex[i], G->Vertex[j]);//打印和v相连的边
			}
		}
	}
}

替换顶点 

        如你所见代码是将某个顶点替换成某个顶点

void PutVex(MGraph* G, Vertextype v ,Vertextype value)//替换顶点,将v换成value
{
	int i;
	for (i = 0; i < G->vernum; i++)//去边集里面找v
	{
		if (v == G->Vertex[i])
		{
			G->Vertex[i] = value;
			printf("将%c替换%c成功", v, value);
			return;
		}
	}
	if (i == G->vernum)
	{
		printf("没找到:%c\n",v);
	}
}

新增顶点

         如你所见代码是在图中新增一个顶点

void InsertVex(MGraph* G, Vertextype v)//新增顶点v
{
	if (G->vernum == MAXVertexNum)
	{
		printf("图满了,存不下了\n");
		return;
	}
	int i, j;
	G->Vertex[G->vernum] = v;
	G->vernum++;
	//初始化和顶点的边
	G->Edge[G->vernum - 1][G->vernum - 1] = 0;
	for (i = 0; i < G->vernum - 1; i++)
	{
		G->Edge[i][G->vernum - 1] = 32767;
		G->Edge[G->vernum - 1][i] = 32767;
	}
	//输入边,G->arcnum(边的数量)要++
	printf("请输入想要与顶点新增的边的序号和权值(输入-1和-1结束)\n");
	while (1)
	{
		scanf_s("%d%d", &j, &i);//用j来表示与之相连的顶点存储位置(下标=位置-1)i来存储权值
		if (j == -1 && i == -1)break; 
		else if (j<1 || j>G->vernum)
		{
			printf("输入错误,请重新输入\n");
			continue;
		}
		G->Edge[G->vernum - 1][j-1] = i;//确定边的关系
		G->Edge[j - 1][G->vernum - 1] = i;
		G->arcnum++;
	}
}

打印矩阵的边的权值

        将所有的权值打印出来,直观的表示

void PrintMGraph(MGraph* G)//打印矩阵
{
	int i, j;
	printf("图的矩阵表示为:\n");
	for (i = 0; i < G->vernum; i++)
	{
		for (j = 0; j < G->vernum; j++)
		{
			printf("%d\t", G->Edge[i][j]);//第i行第j列
		}
		printf("\n");
	}
}

深度优先遍历

        认真听,认真听,重头戏来了

深度优先遍历,我先用一个visited数组用来保存节点是否被访问,具体代码如下:

/*
深度优先搜索,遍历算法
*/
int visited[MAXVertexNum] = { 0 };//用于记录数组节点是否被访问,访问了就变为1
void DFS(MGraph* G, int i)
{
	visited[i] = 1;//访问过的顶点标记为1
	printf("%c ", G->Vertex[i]);//在进行遍历之前打印访问的顶点
	for (int j = 0; j < G->vernum; j++)//从第0个顶点开始判断,直到最后一个顶点
	{
		if (!visited[j] && G->Edge[i][j] == 1)//若顶点vexs[j]与顶点vexs[i]相连,并且vexs[j]没有访问过
		{
			DFS(G, j);//那就访问vexs[j]
		}
	}
	//printf("%c ", G->Vertex[i]);//如果写在最后,则逆序输出,可以将上面的cout注释掉试一下
}
void DFSTraverseAL(MGraph *G)
{
	for (int i = 0; i < G->vernum; i++) //从vexs[0]开始进行深度优先递归,若是连通图,只会执行一次DFS(G,0)
	{
		if (!visited[i])//判断是否已被访问
		{
			DFS(G, i);
		}
	}
	/*
    //因为深度优先递归后每个visited[i]都是1,不会再执行if了
     //若是非连通图,可能会执行到DFS(G,1),DFS(G,2),DFS(G,3),DFS(G,4)*/
}

广度优先遍历

        由于上面没有涉及到队,但是不代表不会用到,在广度优先遍历中,我用队来实现遍历。

因为队这种数据结构,后进后出,很好的切合了广度优先遍历(也就是层次遍历),所以定义了队,话不多说请看代码:

typedef struct LQueueNode//队节点
{
	int data;//队的数据类型
	struct LQueueNode* next;
}LQueueNode;
typedef struct LQueue//队
{
	LQueueNode* front;//队的头指针
	LQueueNode* rear;//尾指针
}LQueue;
void InitLQueue(LQueue* Q)//队的初始化
{
	LQueueNode* p = (LQueueNode*)malloc(sizeof(LQueueNode));
	if (p == NULL)
		exit(-1);
	p->next = NULL;
	Q->front = p;
	Q->rear = p;
}
void PushLQueue(LQueue* Q,int x)//入队操作
{
	LQueueNode* p = (LQueueNode*)malloc(sizeof(LQueueNode));
	if (p == NULL)
		exit(-1);
	p->data = x;
	p->next = NULL;
	Q->rear->next = p;
	Q->rear = p;
}
int PopLQueue(LQueue* Q)//出队操作
{
	if (Q->front->next == NULL)
	{
		printf("队为空\n");
		return -1;
	}
	else
	{
		LQueueNode* r = Q->front->next;
		if (Q->front->next->next == NULL)//如果只有一个元素的话,头删会使得尾指针丢失变为野指针
		{
			Q->rear = Q->front;
		}
		int n = r->data;
		Q->front->next = Q->front->next->next;
		free(r);
		r = NULL;
		return n;
	}
}
int LQueueEmpty(LQueue* Q)//判断队列是否为空
{
	if (Q->front->next == NULL)
		return 0;//空为0
	else
		return 1;//非空为1
}

 相信大家已经队这种数据结构已经掌握了,这里就不细说了,请看重头戏

void BFSraverseAL(MGraph* G)
{
	int i, j;
	for (i = 0; i < G->vernum; i++)//初始化保存标志位的信息
		visited[i] = 0;
	LQueue Q;
	InitLQueue(&Q);
	for (i = 0; i < G->vernum; i++)
	{							   
		if (!visited[i])//未访问过 该顶点
		{
			visited[i] = 1;
			printf("%c ", G->Vertex[i]);
			PushLQueue(&Q, i);
			while (LQueueEmpty(&Q))
			{
				i = PopLQueue(&Q); //将队中元素出队列,赋值给i
				for (j = 0; j < G->vernum; j++)
				{
					if (!visited[j] && G->Edge[i][j])//其他顶点与该顶点存在边
					{
						visited[j] = 1;
						printf("%c ", G->Vertex[j]);
						PushLQueue(&Q, j);
					}
				}
			}
		}
	}
}

完整代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#pragma warning(disable:6386)//忽略6386错误,(这里我使用的编译器是vs2019不清楚为什么会有6386报错)
#define MAXVertexNum 20//顶点数目最大值
typedef char Vertextype;//顶点的数据类型
typedef int Edgetype;//带权图中边上权值的数据类型
typedef struct
{
	Vertextype Vertex[MAXVertexNum];//顶点表
	Edgetype Edge[MAXVertexNum][MAXVertexNum];//邻接矩阵,边表
	int vernum, arcnum; //图的顶点数和弧数
}MGraph;
typedef struct LQueueNode//队节点
{
	int data;//队的数据类型
	struct LQueueNode* next;
}LQueueNode;
typedef struct LQueue//队
{
	LQueueNode* front;//队的头指针
	LQueueNode* rear;//尾指针
}LQueue;
void CreateMGraph(MGraph *G)//图的建立
{
	char cls;//用于清除缓冲区的回车
	int i, j, w, k;
	printf("请输入顶点的数量\n");
	scanf_s("%d", &(G->vernum));
	cls = getchar();//清除缓冲区回车
	printf("请输入边的条数\n");
	scanf_s("%d", &(G->arcnum));
	cls = getchar();
	printf("请输入顶点信息\n");
	for (i = 0; i < G->vernum; i++)//给存储顶点数组赋值
	{
		scanf_s("%c", &(G->Vertex[i]), 1);
	}
	cls = getchar();//清除缓冲区回车
	for (i = 0; i < G->vernum; i++)//初始化边的关系
	{
		for (j = 0; j < G->vernum; j++)
		{
			if (j == i)
			{
				G->Edge[i][j] = 0;//这里我把指向自己的边权值赋值为0
			}
			else
			{
				G->Edge[i][j] = 32767;//指向别人的边赋值为32767
			}
		}
	}
	for (k = 0; k < G->arcnum; k++)//给边赋值
	{
		printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
		scanf_s("%d%d%d", &i, &j, &w);
		cls = getchar();
		G->Edge[i - 1][j - 1] = w;
		G->Edge[j - 1][i - 1] = w;//无向图具有对称性
	}
}
void GetVex(MGraph* G, Vertextype v)//找到顶点v并返回v的相关信息
{
	int i, j;
	for (i = 0; i < G->vernum; i++)
	{
		if (v == G->Vertex[i])
		{
			break;//v存在的话就退出
		}
	}
	if (i == G->vernum)//判断v是否存在
	{
		printf("没找到:%c\n", v);
		return;
	}
	else
	{
		printf("顶点在第:%d个位置\n", i + 1);//打印v在的位置
		for (j = 0; j < G->vernum; j++)
		{
			if (G->Edge[i][j] != 0 && G->Edge[j][i] != 32767)//打印和v存在边关系的vertex[j]
			{
				printf("%c和%c存在边\n", G->Vertex[i], G->Vertex[j]);//打印和v相连的边
			}
		}
	}
}

void PutVex(MGraph* G, Vertextype v ,Vertextype value)//替换顶点,将v换成value
{
	int i;
	for (i = 0; i < G->vernum; i++)//去边集里面找v
	{
		if (v == G->Vertex[i])
		{
			G->Vertex[i] = value;
			printf("将%c替换%c成功", v, value);
			return;
		}
	}
	if (i == G->vernum)
	{
		printf("没找到:%c\n",v);
	}
}

void InsertVex(MGraph* G, Vertextype v)//新增顶点v
{
	if (G->vernum == MAXVertexNum)
	{
		printf("图满了,存不下了\n");
		return;
	}
	int i, j;
	G->Vertex[G->vernum] = v;
	G->vernum++;
	//初始化和顶点的边
	G->Edge[G->vernum - 1][G->vernum - 1] = 0;
	for (i = 0; i < G->vernum - 1; i++)
	{
		G->Edge[i][G->vernum - 1] = 32767;
		G->Edge[G->vernum - 1][i] = 32767;
	}
	//输入边,G->arcnum(边的数量)要++
	printf("请输入想要与顶点新增的边的序号和权值(输入-1和-1结束)\n");
	while (1)
	{
		scanf_s("%d%d", &j, &i);//用j来表示与之相连的顶点存储位置(下标=位置-1)i来存储权值
		if (j == -1 && i == -1)break; 
		else if (j<1 || j>G->vernum)
		{
			printf("输入错误,请重新输入\n");
			continue;
		}
		G->Edge[G->vernum - 1][j-1] = i;//确定边的关系
		G->Edge[j - 1][G->vernum - 1] = i;
		G->arcnum++;
	}
}
void PrintMGraph(MGraph* G)//打印矩阵
{
	int i, j;
	printf("图的矩阵表示为:\n");
	for (i = 0; i < G->vernum; i++)
	{
		for (j = 0; j < G->vernum; j++)
		{
			printf("%d\t", G->Edge[i][j]);//第i行第j列
		}
		printf("\n");
	}
}
/*
深度优先搜索,遍历算法
*/
int visited[MAXVertexNum] = { 0 };//用于记录数组节点是否被访问,访问了就变为1
void DFS(MGraph* G, int i)
{
	visited[i] = 1;//访问过的顶点标记为1
	printf("%c ", G->Vertex[i]);//在进行遍历之前打印访问的顶点
	for (int j = 0; j < G->vernum; j++)//从第0个顶点开始判断,直到最后一个顶点
	{
		if (!visited[j] && G->Edge[i][j] == 1)//若顶点vexs[j]与顶点vexs[i]相连,并且vexs[j]没有访问过
		{
			DFS(G, j);//那就访问vexs[j]
		}
	}
	//printf("%c ", G->Vertex[i]);//如果写在最后,则逆序输出,可以将上面的cout注释掉试一下
}
void DFSTraverseAL(MGraph *G)
{
	for (int i = 0; i < G->vernum; i++) //从vexs[0]开始进行深度优先递归,若是连通图,只会执行一次DFS(G,0)
	{
		if (!visited[i])//判断是否已被访问
		{
			DFS(G, i);
		}
	}
	/*
    //因为深度优先递归后每个visited[i]都是1,不会再执行if了
     //若是非连通图,可能会执行到DFS(G,1),DFS(G,2),DFS(G,3),DFS(G,4)*/
}
void InitLQueue(LQueue* Q)//队的初始化
{
	LQueueNode* p = (LQueueNode*)malloc(sizeof(LQueueNode));
	if (p == NULL)
		exit(-1);
	p->next = NULL;
	Q->front = p;
	Q->rear = p;
}
void PushLQueue(LQueue* Q,int x)//入队操作
{
	LQueueNode* p = (LQueueNode*)malloc(sizeof(LQueueNode));
	if (p == NULL)
		exit(-1);
	p->data = x;
	p->next = NULL;
	Q->rear->next = p;
	Q->rear = p;
}
int PopLQueue(LQueue* Q)//出队操作
{
	if (Q->front->next == NULL)
	{
		printf("队为空\n");
		return -1;
	}
	else
	{
		LQueueNode* r = Q->front->next;
		if (Q->front->next->next == NULL)//如果只有一个元素的话,头删会使得尾指针丢失变为野指针
		{
			Q->rear = Q->front;
		}
		int n = r->data;
		Q->front->next = Q->front->next->next;
		free(r);
		r = NULL;
		return n;
	}
}
int LQueueEmpty(LQueue* Q)//判断队列是否为空
{
	if (Q->front->next == NULL)
		return 0;//空为0
	else
		return 1;//非空为1
}

void BFSraverseAL(MGraph* G)
{
	int i, j;
	for (i = 0; i < G->vernum; i++)//初始化保存标志位的信息
		visited[i] = 0;
	LQueue Q;
	InitLQueue(&Q);
	for (i = 0; i < G->vernum; i++)
	{							  
		if (!visited[i])//未访问过 该顶点
		{
			visited[i] = 1;
			printf("%c ", G->Vertex[i]);
			PushLQueue(&Q, i);
			while (LQueueEmpty(&Q))
			{
				i = PopLQueue(&Q); //将队中元素出队列,赋值给i
				for (j = 0; j < G->vernum; j++)
				{
					if (!visited[j] && G->Edge[i][j])//其他顶点与该顶点存在边
					{
						visited[j] = 1;
						printf("%c ", G->Vertex[j]);
						PushLQueue(&Q, j);
					}
				}
			}
		}
	}
}
/*//一组数据
4
4
ABCD
1 2 1
1 3 1
2 3 1
3 4 1


*/
int main()
{
	MGraph G;
	CreateMGraph(&G);//图的建立
	//GetVex(&G, 'A'); //找到顶点A并返回A的相关信息
	//PutVex(&G, 'A', 'a');//替换顶点,将A换成a
	//InsertVex(&G, 'D');//新增顶点函数
	DFSTraverseAL(&G);
	printf("\n");
	BFSraverseAL(&G);
	printf("\n");
	PrintMGraph(&G);//矩阵的打印
	return 0;
}

  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 邻接矩阵深度优先遍历广度优先遍历的两种遍历方式。 深度优先遍历是从起点开始,沿着一条路径一直走到底,直到不能再走为止,然后返回上一个节点,继续走其他路径,直到所有节点都被访问过为止。在邻接矩阵中,可以使用递归或栈来实现深度优先遍历广度优先遍历是从起点开始,先访问起点的所有邻居节点,然后依次访问它们的邻居节点,直到所有节点都被访问过为止。在邻接矩阵中,可以使用队列来实现广度优先遍历。 ### 回答2: 邻接矩阵的一种常见表示方法,其中通过二维数组来表示的节点之间的关系。在邻接矩阵中,每个节点(或者说顶点)的行代表其所在的节点,而列则代表其与其他节点之间是否存在连边。如果该节点与另一个节点存在连边,则邻接矩阵中该行、该列的交叉处值为1,反之,如果不存在连边,则该交叉处值为0。基于邻接矩阵,我们可以进行深度优先遍历广度优先遍历深度优先遍历(DFS)是一种对进行遍历的方式,遵循“先访问子节点再访问兄弟节点”的原则。具体来说,该算法从某个节点开始遍历,访问其第一个未访问的子节点,对该子节点执行DFS遍历,直到到达某个叶子节点为止。然后回溯,访问该节点的下一个未访问的子节点,继续执行DFS遍历,直到所有子节点都被遍历完毕。DFS遍历过的节点,会在遍历结束后形成一个连通块。对于邻接矩阵,我们可以通过递归实现DFS遍历: ``` void dfs(int u) { visited[u] = true; // 标记该节点已被访问 for(int v = 0; v < n; v++) { // 枚举该节点的所有邻接点 if(!visited[v] && matrix[u][v]) { // 如果该邻接点未被访问且与该节点相邻 dfs(v); // 则访问该邻接点 } } } ``` 广度优先遍历(BFS)也是一种对进行遍历的方式,遵循“先访问距离起点最近的节点”的原则。具体来说,该算法从某个节点开始遍历,将其入队,然后访问其所有的邻接点,并将其邻接点入队。然后从队首取出下一个节点,重复上述步骤,直到所有的节点都被遍历完毕。BFS遍历过的节点,会在遍历结束后形成一棵广度优先搜索树。对于邻接矩阵,我们可以通过队列实现BFS遍历: ``` void bfs(int u) { queue<int> q; q.push(u); // 将起点入队 visited[u] = true; // 标记该节点已被访问 while(!q.empty()) { // 如果队列非空 int u = q.front(); // 取出队首节点 q.pop(); for(int v = 0; v < n; v++) { // 枚举该节点的所有邻接点 if(!visited[v] && matrix[u][v]) { // 如果该邻接点未被访问且与该节点相邻 visited[v] = true; // 标记该节点已被访问 q.push(v); // 将该邻接点入队 } } } } ``` 总之,邻接矩阵的一种常见表示方法,DFS和BFS遍历是对进行遍历的常用算法,它们可以通过递归和队列实现。掌握邻接矩阵的DFS和BFS遍历,可以更好地理解的相关算法数据结构,也有助于解决各种实际问题。 ### 回答3: 邻接矩阵的一种常见存储方式。深度优先遍历(DFS)和广度优先遍历(BFS)是针对进行的两种遍历方式。下面将详细解释邻接矩阵的DFS和BFS的过程和实现方法。 邻接矩阵的DFS遍历: 深度优先遍历通常使用递归的方式来实现。具体的深度优先遍历邻接矩阵的过程如下: 1. 从一个顶点开始进行遍历; 2. 访问该顶点,并标记该顶点为已访问; 3. 从该顶点出发,找到一个未被访问过的相邻顶点,标记该顶点为已访问,然后递归到该顶点继续递归遍历; 4. 如果该顶点没有相邻未访问过的顶点,则回溯到最近一个有未访问过相邻顶点的顶点,然后继续递归遍历其它相邻顶点。 邻接矩阵的BFS遍历: 广度优先遍历则使用队列的方式来实现。具体的广度优先遍历邻接矩阵的过程如下: 1. 从一个顶点开始进行遍历; 2. 将该顶点加入队列中,并标记该顶点为已访问; 3. 从队列中取出一个未被访问过的顶点,访问该顶点,并将其未被访问过的相邻顶点加入队列中,并标记这些顶点为已访问; 4. 重复步骤3直到队列为空。 邻接矩阵的DFS和BFS的时间复杂度都为O(V^2),其中V为顶点数。DFS相对BFS来说,更适用于查找路径的问题,快速查找到节点之间的路径信息;BFS适用于确定最短路径和寻找最近解问题。 总而言之,邻接矩阵是一种常见的的存储方式,DFS和BFS是对的两种常用遍历方式。它们各自有适用性,要根据具体情况进行选择。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值