目录
前言
接下来的文章主要介绍一下图储存结构中的邻接矩阵和邻接矩阵的深度优先搜索,文章通俗易懂,应该都能都看懂,文章也存在一些问题,希望大家看到后留言一起讨论一下
一、图是什么?
图是一种比线性表和树更为复杂的数据结构,在图的结构中,节点可以是任意的,图中任意两个数据元素都可那能相关。图的应用广泛,话不多说,直接上代码。
二、图的存储结构之邻接矩阵
1.邻接矩阵储存表示
代码如下(示例):
typedef char VerType;
typedef int AreType;
typedef struct graph
{
AreType b;//边的数目
AreType d;//顶点的数目
VerType* Ver;//一级指针,后面的开辟空间储存顶点
AreType** Are;//二级指针,开辟空间后记录边的两个顶点
}Graph;
2.创建邻接矩阵表示图
思路:(一般有思路才能做下去,做什么都是先将思路理清楚再开始做)
1.首先将一级指针和二级指针开辟空间
2.创建成功之后,将顶点输入,然后初始化二级指针
代码如下(示例):
void CreatGraph(Graph& G)
{
char input;
G.Ver = (char*)malloc(sizeof(char) * G.d);//分配储存字符顶点的空间
G.Are = (int**)malloc(sizeof(int*) * G.d); //分配储存边的行空间
for (int i = 0; i < G.d; i++)
{
G.Are[i] = (int*)malloc(sizeof(int) * G.d);//分配列空间
}
if ((!G.Ver) && (!G.Are))//分配不成功退出
return;
for (int i = 0; i < G.d; i++)
{
printf("请输入第%d个顶点\n", i + 1);
scanf("%c", &input);
getchar();//用getchar()吃掉回车符,下述操作一致
G.Ver[i] = input;
}
for (int i = 0; i < G.d; i++)
{
for (int j = 0; j < G.d; j++)//对邻接矩阵进行初始化
{
G.Are[i][j] = 0;
}
}
}
3. 确定图的边
思路:
1.先将图的边进行输入
2.在邻接矩阵上确定边,用矩阵的方式储存,下图示
代码如下(示例):
void WxT(Graph G) //邻接无向图
{
char b1, b2;
for (int k = 0; k < G.b; k++)
{
printf("请输入第%d边的两个顶点\n", k + 1);
scanf("%c,%c", &b1, &b2);
getchar();
int i = Local(G, b1);//通过Local返回点b1,b2位置
int j = Local(G, b2);
G.Are[i][j] = 1;
G.Are[j][i] = 1; //因为是无向图,所以要有对称,此处的标记随便设置,不一定就是1
}
}
同时这里也将返回位置的函数给出,供大家参考
int Local(Graph G,char b)
{
for (int i = 0; i < G.d; i++)
{
if (G.Ver[i]==b)//遍历寻找边的位置
{
return i;
}
}
}
三、图的深度优先搜索
1.深度优先过程
- 深度优先搜索( Depth First Search , DFS )遍历类似于树的先序遍历,是树的先序遍历的推广
- 对于一个连通图,深度优先搜索遍历的过程如下。
- 从图中某个顶点 v 出发,访问 v
- 找出刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。
- 返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点。
- 重复步骤(2)和步骤(3),直至图中所有顶点都被访问过,搜索结束。
代码示例:
int arr[20] = { 0 };//标记数组
void Dfs(Graph G,int v)//深度优先搜索,连通图可用
{
printf("%c", G.Ver[v]);//搜索开始
arr[v] = 1;//找到之后就做一个标记,下次不再寻找
for (int i = 0; i < G.d; i++)
{
if (G.Are[v][i] != 0 && arr[i] == 0)//如果邻接矩阵第v行第i个不为0,且未被寻找
Dfs(G, i);//递归调用
}
}
上述代码只是针对连通图,但不可能所有的图都是连通图,这里就牵涉到了非连通图
思路:
判断所定义的数组标记是否为0(自定义),如果为0,表明此节点还未被访问,调用连通图函数
代码示例:
void dfs_not(Graph G)//非连通图的的深度优先遍历
{
for (int i = 0; i < G.d; i++)
{
if (arr[i] == 0)//非连通图只要是还未被寻找就调用Dfs进行搜索
Dfs(G, i);
}
}
到这里图的邻接矩阵和深度优先搜索基本就结束了。
四、完整代码
下面将自己做的邻接矩阵小系统完整代码附上,可能会有问题,希望大家看到问题能够留言我们一起解决一下
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include <string.h>
typedef char VerType;
typedef int AreType;
typedef struct graph
{
AreType b;
AreType d;
VerType* Ver;
AreType** Are;
}Graph;
int Local(Graph G,char b)
{
for (int i = 0; i < G.d; i++)
{
if (G.Ver[i]==b)//遍历寻找边的位置
{
return i;
}
}
}
void Visit(Graph G)
{
for (int i = 0; i < G.d; i++)
{
printf("%-2c", G.Ver[i]);//打印输入的顶点
}
printf("\n");
for (int i = 0; i < G.d; i++)
{
for (int j = 0; j < G.d; j++)
{
printf("%-2d", G.Are[i][j]);//打印邻接矩阵
}
printf("\n");
}
}
void CreatGraph(Graph& G)
{
char input;
G.Ver = (char*)malloc(sizeof(char) * G.d);//分配储存字符顶点的空间
G.Are = (int**)malloc(sizeof(int*) * G.d); //分配储存边的行空间
for (int i = 0; i < G.d; i++)
{
G.Are[i] = (int*)malloc(sizeof(int) * G.d);//分配列空间
}
if ((!G.Ver) && (!G.Are))//分配不成功退出
return;
for (int i = 0; i < G.d; i++)
{
printf("请输入第%d个顶点\n", i + 1);
scanf("%c", &input);
getchar();//用getchar()吃掉回车符,下述操作一致
G.Ver[i] = input;
}
for (int i = 0; i < G.d; i++)
{
for (int j = 0; j < G.d; j++)//对邻接矩阵进行初始化
{
G.Are[i][j] = 0;
}
}
}
void WxT(Graph G) //邻接无向图
{
char b1, b2;
for (int k = 0; k < G.b; k++)
{
printf("请输入第%d边的两个顶点\n", k + 1);
scanf("%c,%c", &b1, &b2);
getchar();
int i = Local(G, b1);//通过Local返回点b1,b2位置
int j = Local(G, b2);
G.Are[i][j] = 1;
G.Are[j][i] = 1; //因为是无向图,所以要有对称,此处的标记随便设置,不一定就是1
}
}
void YxT(Graph G) //邻接有向图
{
char b1, b2;
for (int k = 0; k < G.b; k++)
{
printf("请输入第%d边的两个顶点\n", k + 1);
scanf("%c,%c", &b1, &b2);
getchar();
int i = Local(G, b1);
int j = Local(G, b2);
G.Are[i][j] = 1;
}
}
void WxW(Graph G) //邻接无向网
{
char b1, b2;
int n;
for (int k = 0; k < G.b; k++)
{
printf("请输入第%d条边的两个顶点和权值\n", k + 1);
scanf("%c,%c,%d", &b1, &b2,&n);
getchar();
int i = Local(G, b1);
int j = Local(G, b2);
G.Are[i][j] = n;
G.Are[j][i] = n;
}
}
void YxW(Graph G) //邻接有向网
{
char b1, b2;
int n;
for (int k = 0; k < G.b; k++)
{
printf("请输入第%d条边的两个顶点和权值\n", k + 1);
scanf("%c,%c,%d", &b1, &b2, &n);
getchar();
int i = Local(G, b1);
int j = Local(G, b2);
G.Are[i][j] = n;
//G.Are[j][i] = n;
}
}
int arr[20] = { 0 };//标记数组
void Dfs(Graph G,int v)//深度优先搜索,连通图可用
{
printf("%c", G.Ver[v]);//搜索开始
arr[v] = 1;//找到之后就做一个标记,下次不再寻找
for (int i = 0; i < G.d; i++)
{
if (G.Are[v][i] != 0 && arr[i] == 0)//如果邻接矩阵第v行第i个不为0,且未被寻找
Dfs(G, i);//就递归调用
}
}
void dfs_not(Graph G)//非连通图的的深度优先遍历
{
for (int i = 0; i < G.d; i++)
{
if (arr[i] == 0)//非连通图只要是还未被寻找就调用Dfs进行搜索
Dfs(G, i);
}
}
int main()
{
int d, b,input,dfs_num;
Graph G;
while (true)
{
int arr[20] = { 0 };
printf("请输入你的选择:\n1.创建无向图 2.创建有向图 3.创建无向网 4.创建有向网 0.退出:\n");
scanf("%d", &input);
getchar();
if (0 == input)
break;
printf("请输入顶点数,和边数:");
scanf("%d %d", &d, &b);
getchar();
G.d = d;
G.b = b;
switch (input)
{
case 1:
{
CreatGraph(G);
WxT(G);
Visit(G);
printf("请输入深度搜索开始的顶点");
scanf("%d", &dfs_num);
Dfs(G, dfs_num-1);
dfs_not(G);
}break;
case 2:
{
CreatGraph(G);
YxT(G);
Visit(G);
printf("请输入深度搜索开始的顶点");
scanf("%d", &dfs_num);
Dfs(G, dfs_num-1);
dfs_not(G);
}break;
case 3:
{
CreatGraph(G);
WxT(G);
Visit(G);
printf("请输入深度搜索开始的顶点");
scanf("%d", &dfs_num);
Dfs(G, dfs_num-1);
dfs_not(G);
}break;
case 4:
{
CreatGraph(G);
WxW(G);
Visit(G);
printf("请输入深度搜索开始的顶点");
scanf("%d", &dfs_num);
Dfs(G, dfs_num-1);
dfs_not(G);
}
break;
}
}
printf("已退出");
return 0;
}
总结
到这里图的邻接矩阵和深度优先搜索就结束了,遇到了问题好多,好在后面都慢慢解决了,遇到这种难的问题不要退缩,要迎难而上,这样解决问题之后成就感真的慢满满