实验内容
(题目)
实验内容:建立一个图,对图进行深度优先遍历及广度优先遍历。
基本内容:只对无向图进行深度和广度优先遍历。
选做内容:
计算图中两个顶点之间的最短路径,如果不存在路径,则提示没有路径。
- 基本内容:
实验思路:
我采取邻接表的方式存储图
深度优先遍历:采取递归过程,访问顶点。v后,选择一个与v邻接的未被访问的结点u,再从u继续开始深度优先遍历(此处进行递归调用),若从任一顶点出发,再也无法抵达一个未被访问过的邻接结点,则本次搜索结束。
BFS:采取非递归方式,借助了队这一数据结构。访问出发结点v,并让v入队。当队不为空时,重复下面的操作:v结点出队,访问v的所有未被访问过的邻接结点,并在访问后让他们入队。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct adjnode
{
int adjdata;
struct adjnode * next;
} adj;
typedef struct nodes
{
int nodedata;
adj * start;
} no;
no a[100];
int visited1[100]= {0};/*第一个辅助数组*/
void dfs(int v)/*对连通图的深度优先遍历*/
{
visited1[v]=1;/*将辅助数组对应的标记值赋为1*/
printf("%d",a[v].nodedata);/*打印该节点对应的数值*/
adj * p=a[v].start;/*从自己邻接表中的邻接链表开始遍历所有他所邻接的节点*/
while (p!=NULL)
{
if (visited1[p->adjdata]==0)dfs(p->adjdata);/*已经遍历过的节点除外,对未遍历的进行递归*/
else p=p->next;/*这一条链让其走完*/
}
}
void Traversal_dfs(int nodenum)/*查看所有的联通图是不是都有进行遍历*/
{int a=1;
for (int i=1; i<=nodenum; i++)
{
if (visited1[i]==1)continue;
else
{
dfs(i);printf(" ");
}
}
}
int queue[100]= {0};
int visited2[100]= {0};/*第二个辅助数组*/
void bfs(int v)
{
int rear=0,front=-1,i=0;
visited2[v]=1;/*将辅助数组对应位置进行标记*/
printf("%d",a[v].nodedata);/*先输出再入队*/
queue[rear]=a[v].nodedata;/*入队*/
rear++;
adj * p;
while (front!=rear)/*当队伍非空时*/
{
i=queue[front];/*从队首取出队伍中的元素*/
queue[front]=0;/*出队*/
front++;
p=a[i].start;
while (p!=NULL)
{
if (visited2[p->adjdata]==0){
visited2[p->adjdata]=1;/*标记一下已经遍历过该节点了*/
printf("%d",a[p->adjdata].nodedata);
queue[rear]=p->adjdata;
rear++;
}/*如果未被遍历则打印后入队*/
p=p->next;/*向邻接表后移动*/
}
}
}
void Traversal_bfs(int nodenum)/*查看所有的联通图是不是都有进行遍历*/
{
for (int i=1; i<=nodenum; i++)
{
if (visited2[i]==1)continue;
else
{
bfs(i);printf(" ");
}
}
}
int main()
{
int u,v;
no * node,* t,* p;
adj * temp,* te;
/*创建一个邻接表*/
int nodenum/*节点数*/,arcnum/*边数*/,i=1,j=0;
printf("请输入节点数目和边数:");
scanf("%d",&nodenum);
scanf("%d",&arcnum);
/*初始化节点*/
while (i<=nodenum)
{
a[i].nodedata=i;
a[i].start=NULL;
i++;
}
/*输入边数*/
printf("请输入边:\n");
while (j<arcnum)
{
scanf("%d %d",&u,&v);
temp=(adj*)malloc(sizeof(adj));
temp->adjdata=v;
temp->next=a[u].start;/*将v头插添加到u的链里面*/
a[u].start=temp;
temp=(adj*)malloc(sizeof(adj));
temp->adjdata=u;
temp->next=a[v].start;/*将u头插添加到v的链里面*/
a[v].start=temp;
j++;
}
printf("深度优先遍历:");
dfs(3);
/*Traversal_dfs(nodenum);*/
printf("\n");
printf("广度优先遍历:");
bfs(3);
/*Traversal_bfs(nodenum);*/
printf("\n");
return 0;
}
运行结果:
输入数据:
请输入节点数目和边数:8 10
请输入边:
1 2
1 3
2 4
2 5
3 6
3 7
4 8
5 8
6 8
7 8
输出结果:
- 选做内容
实验思路:
用邻接矩阵进行图的存储,此处图是有向图。
先初始化d数组,里面存储的是所查询的节点到各节点的最小值,也就是最短路径的长度,将其中每个值赋为max;p数组,里面存储的是从所查询节点到各节点的最短路径之间的各节点的前驱节点,第一次遍历的时候,当查询节点与遍历的节点之间有路,就将这一遍历节点对应的p数组的位置赋成所查询的节点,s数组是已经访问过的节点的集合,初始化时因为除了搜索节点是其中的元素,将其对应位置赋成1意外其他节点赋为0;
求各节点的最早开始时间,进入循环,循环重复节点数目-1次,下面解释循环内的程序:
先取搜索的节点,然后找到他邻接的节点,将每个与他邻接的节点的值加上当前搜索节点到他的最短值若小于他现在到搜索节点的值,则更新这个节点的对应d数组的值,并将这个节点记录在p节点内,然后全部遍历过了之后,挑选最小的值进入s数组,在将这个节点作为下一个循环的搜索的节点。
最后用查询的终点进行搜索,将其所有前驱节点入栈,依次出栈即可得到从查询起点到终点的最短路径,并输出其对应的最短路径的权值之和。
代码
#include <stdio.h>
#include <stdlib.h>
#define max 10000
int main()
{
int u,v,w;
/*创建一个邻接矩阵*/
int nodenum/*节点数*/,arcnum/*边数*/,i=0,j=0;
printf("请输入节点数目和边数:");
scanf("%d",&nodenum);
scanf("%d",&arcnum);
/*初始化邻接矩阵*/
int adj[nodenum+1][nodenum+1];
/*初始化矩阵*/
for (i=1; i<nodenum+1; i++)
{
for (j=1; j<nodenum+1; j++)
{
if (i==j)adj[i][j]=0;
else adj[i][j]=max;
}
}
/*输入边数*/
j=0;
printf("请输入边和权值:\n");
while (j<arcnum)
{
scanf("%d%d%d",&u,&v,&w);
adj[u][v]=w;
adj[v][0]++;
adj[0][u]++;
j++;
}
printf("请输入你想要查询的两个节点:");
scanf("%d %d",&u,&v);
/*进行迪杰斯特拉算法*/
/*先把矩阵中的最短路径长度辅助数组d和顶点序列数组p进行初始化*/
int minlen=0,q=0;
int d[nodenum+1],p[nodenum+1]/*前面的链接节点*/,s[nodenum+1];
/*初始化p,s*/
for (i=1;i<nodenum+1;i++){
p[i]=0;
}
for (i=1;i<nodenum+1;i++){
s[i]=0;
}
for (i=1;i<nodenum+1; i++)
{
d[i]=adj[u][i];
if (d[i]<max)
{
p[i]=u;
}
}
s[u]=1;
int count=1;
while (count<nodenum)
{
/*找到目前没放到已遍历最近的那个节点*/
minlen=max;
for(i=1; i<nodenum+1; i++)
{
if (s[i]==0)
{
if (d[i]<minlen)
{
q=i;
minlen=d[i];
}
}
else continue;
}
s[q]=1;
/*开始进行更新最短路径及距离*/
for (i=1; i<nodenum+1; i++)
{
if (s[i]==0&&d[q]+adj[q][i]<d[i])
{
d[i]=d[q]+adj[q][i];
p[i]=q;
}
}
count++;
}
//print d,p,s
printf("下面是三个工具数组的数据展示:\n");
for (int k=1;k<nodenum+1;k++){
printf("%d ",d[k]);
}
printf("\n");
for (int k=1;k<nodenum+1;k++){
printf("%d ",p[k]);
}printf("\n");
for (int k=1;k<nodenum+1;k++){
printf("%d ",s[k]);
}printf("\n\n");
//print 两点最短路径距离,以及两点之间的路径
printf("这两间最短距离为:%d\n",d[v]);
i=v;int stack[nodenum];j=-1/*栈顶指针*/;
j++;stack[j]=i;/*让最后的那个节点先入栈*/
printf("这两间最短路径为:");
while (i!=u){
j++;stack[j]=p[i];
i=p[i];
}
//退栈
while(j>=0){
printf("v%d ",stack[j]);
j--;
}
return 0;
}
运行结果:
输入数据:
请输入节点数目和边数:9 11
请输入边和权值:
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
请输入你想要查询的两个节点:1 9
输出结果:
实验总结与反思:
1. 掌握了了邻接表的建立和插入操作。
2. 掌握了图的深度优先遍历和广度优先遍历操作。
3. 在基本任务中我使用了邻接表的方式存储图,体会到了邻接表相较于邻接矩阵的特点和给算法带来的效率提升。
4. 在选做任务中使用了邻接矩阵存储图来计算最短路径。我在两个程序中使用了不同的存储方式来存储图,让我对图的存储方式有了更深的理解,每种存储方式都有自己的特点,需要根据需求的不同来选择不同的存储方式。
5. 学习了计算图最短路径的几种算法,如弗洛伊德和迪杰斯特拉算法,并体会到了不同算法之间的优缺点。