- 实验内容、基本原理
- 实验内容
一幅图共有6个结点,其值为字符A-F,构成如下图所示网络。
编程实现以下功能:①采用邻接矩阵进行存储。②采用prim算法生成最小生成树,输出所有选取的边。
2.实验原理①图
一个图G是由两个集合V和E组成的,V是有限的非空顶点集,E是V上的顶点对所构成的边集,分别用V(G)和E(G)来表示图中的顶点集和边集,用二元组G=(V,E)来表示图G。
②图的存储结构:邻接矩阵表示法
设无向图G=(V,E)具有n个顶点,用顺序方式或链接方式来存储图的顶点表v0,v1,v2,...,vn-1,在顶点表中,每个顶点的信息要根据实际运算而定,它可以是顶点顺序号或其他类型的数据信息。图的边用一个二维数组(n×n阶矩阵,表示结之间的相邻关系)来表示。则G的邻接矩阵被定义为具有如下性质的n阶方阵A:
- 若顶点vi、vj之间无边,则aij=0;
- 若顶点vi、vj之间有边,(vi,vj)是G的边,则aij=1。这时,称矩阵A=(aij)为图的邻接矩阵。矩阵A中的行号、列号对应于图中顶点的序号。③深度优先搜索(DFS)
假设初始状态是图中所有顶点都未曾访问,则深度优先搜索可以从图中某个顶点vi出发,访问此顶点,然后依次从vi的未被访问的邻接点出发,深度优先遍历图,直至图中所有和vi有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问到则另选图中一个未被访问的顶点作为起始点,重复上述过程,直到图中所有顶点都被访问到为止。这种算法叙述如下:
(1)设P为搜索指针,使P指向结点v。
(2)访问P结点,然后使P指向与P结点相邻接的且尚未被访问的结点。
(3)若P不空,则重复步骤(2),否则执行步骤(4)。(4)沿着刚才访问的次序,反向回溯到一个尚有邻接顶点未被访问过的顶点,并使P指向这个未被访问的顶点,然后重复步骤(2),直至所有的顶点均被访问为止。
- 实验步骤和结果
- 步骤①定义结构体类型graph,存储图的信息。包含一个字符类型的数组vexs[]用来储存图的各个顶点的信息,一个整型的二维数组edges[][](邻接矩阵)用来存储图的边的信息,两个整型变量n和e分别存储图的顶点数和边数。全局定义一个整型数组visid[]用来标记图中各个顶点,用于后续对图的各个顶点的访问,visid[]值为0表示未访问过该顶点,值为1表示已经进行过访问。②creategraph函数:函数接收graph类型的指针变量并传递给graph类型的指针变量ga。定义整型变量w作为权值(因为这是一个无权图,所以为w赋值为1)。键入两个整型变量分n、e别作为图的顶点数和边数。运用for循环,将n个顶点的信息存储在图的vexs[]中。先利用for循环为图的邻接矩阵进行初始化,将矩阵置零,之后利用for循环确定图的边的信息,并将其存储在邻接矩阵中(无向图的邻接矩阵是一个对称的矩阵)。③创建DFS函数:函数接收graph类型的指针变量并将其赋值给g。首先用for循环为visid[]数组初始化,将其置零。随后进行循环,顶点有n个就进行n次循环。每次循环中判断visid[i]是否为0,若是,表明该顶点未被进行访问,执行DFSM函数进行访问将指针g和顶点的位置信息i进行传递,若visid[i]为1,则进行下一次的循环。④创建DFSM函数:函数接收graph类型的指针变量并将其传递给g,接收整型类型的变量并将其传递给i(图的顶点的位置信息)。)将g->vexs[i]打印出来进行访问,随后将visid[i]赋值为1,表明第i个顶点已经被访问过。进行循环,利用整型变量j,循环n次,利用邻接矩阵将和第i个顶点联通的所有未被访问的顶点找到,并再次执行DFSM函数,传递指针g和顶点的位置信息j(被找到的即将要进行访问的顶点的位置信息)。
- 结果
- 实验中出现的问题及解决方法
问题:使用scanf为键入字符数值出现问题,程序无法执行。
解决方法:用scanf为字符赋值时会将多余的回车键也输入,需用getchar()将回车键吸收。
- 实验总结及体会
本次实验运用prim算法思想求解图的最小生成树,该思想巧妙地按照将顶点逐个连通的步骤,把顶点加入到已连通的集合中,最后使该集合成为最小生成树。
同时,我接触了图的相关概念,较为抽象,需要多加理解。
并且,本次实验又一次运用到了递归的算法思想,使用递归进行图的顶点的深度优先遍历,希望在日后学习中运用更灵活。
- 附录
#include<stdio.h>
typedef char datatype;
typedef struct{
datatype vexs[100];//顶点信息表
int edges[100][100];//邻接矩阵
int n,e;//顶点数和边数
}graph;
int visid[100];
void creategraph(graph *ga)//建立无向图的邻接表
{
int i,j,k,w=1;
printf("请输入图的顶点数和边数:\n");
scanf("%d%d",&(ga->n),&(ga->e));
printf("请输入顶点信息:\n");
getchar();
for(i=0;i<ga->n;i++)
{
scanf("%c",&(ga->vexs[i]));//输入顶点信息
getchar();
}
for(i=0;i<=ga->n;i++)//邻接矩阵初始化
for(j=0;j<=ga->n;j++)
ga->edges[i][j]=0;
for(k=0;k<ga->e;k++)//读入边的顶点编号和权值,建立邻接矩阵
{
printf("请输入第%d条边起点i终点j(权值为1无需输入):",k+1);
scanf("%d %d",&i,&j);
ga->edges[i][j]=w;
ga->edges[j][i]=w;
}
}
void DFS(graph *g)//深度优先遍历函数
{
int i;
for(i=0;i<g->n;i++)
visid[i]=0;
for(i=0;i<g->n;i++)
if(!visid[i])
DFSM(g,i);
}
void DFSM(graph *g,int i)
{
int j;
printf("深度优先遍历结点:%c\n",g->vexs[i]);
visid[i]=1;
for(j=0;j<g->n;j++)
if((g->edges[i+1][j+1]==1)&&!visid[j])
DFSM(g,j);
}
int main()
{
int i,j;
graph ga;
creategraph(&ga);
printf("邻接矩阵:\n");
for(i=1;i<=ga.n;i++)//输出邻接矩阵
{
for(j=1;j<=ga.n;j++)
printf("%d ",ga.edges[i][j]);
printf("\n");
}
DFS(&ga);
return 0;
}