第1关:实现图的深度优先遍历
任务描述
本关任务:实现 graph.cpp 里的函数void DFS(AdjGraph *G,int v)
,实现图的深度优先遍历。 注意遵守约定:始终从编号小的开始遍历。
相关知识
从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。如果给定图是连通的无向图或者是强连通的有向图,则遍历过程一次就能完成,并可按访问的先后顺序得到由该图的所有顶点组成的一个序列。 深度优先遍历的过程是从图中的某个初始点 v 出发,首先访问初始点 v 然后选择一个与顶点 v 相邻且没被访问过的顶点 w 为初始顶点,再从它出发进行深度优先遍历,直到图中与顶点 v 邻接的所有顶点都被访问过为止,显然这个遍历过程是一个递归过程。 例如,对于图 1 所示的有向图,从顶点 0 开始进行深度优先遍历,可以得到以下访问序列:01243, 03241。
图1
下面以图 1 为例调用 DFS(G,0)
算法,其对应的邻接表如下:
图2
(1). DFS(G,0)
:访问顶点 0,找到顶点 0 相邻的顶点 1 , 它未被访问过,转 (2); (2). DFS(G,1)
:访问顶点 1,找到顶点 1 相邻的顶点 2 , 它未被访问过,转 (3); (3). DFS(G,2)
:访问顶点 2,找到顶点 2 相邻的顶点 4 , 它未被访问过,转 (4); (4). DFS(G,4)
:访问顶点 4,找到顶点 3 相邻的顶点 4 , 它未被访问过,转 (4);
(5). 继续 DFS(G,4)
:其后继结点相邻结点均已被访问,退出 DFS(G,4)
,转 (6) ; (6). 继续 DFS(G,2)
:其后继结点相邻结点均已被访问,退出 DFS(G,2)
,转 (7) ; (7). 继续 DFS(G,1)
:访问顶点 3,其后继结点均被访问,退出 DFS(G,1)
,转 (8) ; (8). 结束。
图采用邻接表的方式存储,结构体如下:
#define MAXV 50
#define INF 32767
typedef struct ANode
{
int adjvex; //该边的邻接点编号
struct ANode *nextarc; //指向下一条边的指针
int weight; //该边的相关信息,如权值(这里用整型表示)
}ArcNode; //边结点类型
typedef struct Vnode
{
//InfoType info //顶点的其他信息
ArcNode *firstarc; //指向第一个边结点
}VNode;
typedef struct
{
VNode adjlist[MAXV]; //邻接表的头结点数组
int n,e; //图中的顶点数n和边数e
}AdjGraph ; //完整的图邻接表类型
编程要求
本关任务是实现 graph.cpp 里的函数void DFS(AdjGraph *G,int v)
以完成邻接表为存储结构的深度优先遍历算法,其中 v 是初始点 visited
是一个全局数组,初始时所有元素均为 0 , 表示所有顶点尚未被访问过。 注意遵守约定:始终从编号小的开始遍历。 测试文件如下: main.cpp
:
#include "graph.h"
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j,n,e;
int A[MAXV][MAXV];
scanf("%d %d",&n,&e);
AdjGraph *G;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&A[i][j]);
}
}
CreateAdj(G,A,n,e);
DFS(G,0);
printf("\n");
DestroyAdj(G);
return 0;
}
测试说明
本关的测试过程如下:
- 平台编译 step3/main.cpp ;
- 平台运行该可执行文件,并以标准输入方式提供测试输入;
- 平台获取该可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。
输入格式: 输入n e,顶点数 边数 输入一个n×n的一个矩阵表示一个图的邻接矩阵,在带权有向图中,用 32767 代替无穷大。
输出格式: 以顶点开始的深度优先遍历序列。
以下是平台对 step3/main.cpp
的测试样例: 样例输入
5 5
0 8 32767 5 32767
32767 0 3 32767 32767
32767 32767 0 32767 6
32767 32767 9 0 32767
32767 32767 32767 32767 0
样例输出 0 1 2 4 3
参考资料
开始你的任务吧,祝你成功!
最后通关代码:
#include "graph.h"
#include <stdio.h>
#include <stdlib.h>
void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e) //创建图的邻接表
{
int i,j;
ArcNode *p;
G=(AdjGraph *)malloc(sizeof(AdjGraph));
for(i=0;i<n;i++) //给邻接表的头元素的指针域置初值
{
G->adjlist[i].firstarc=NULL;
}
for(i=0;i<n;i++) //检查邻接表的每个元素
{
for(j=n-1;j>=0;j--)
{
if(A[i][j]!=0&&A[i][j]!=INF) //存在一条边
{
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
p->adjvex=j; //存放邻接点
p->weight=A[i][j]; //存放权
p->nextarc=G->adjlist[i].firstarc; //采用头插法插入结点p
G->adjlist[i].firstarc=p;
}
}
}
G->n=n;
G->e=e;
}
void DispAdj(AdjGraph *G)
{
int i;
ArcNode *p;
for(i=0;i<G->n;i++)
{
p=G->adjlist[i].firstarc;
printf("%3d: ",i);
while(p!=NULL)
{
printf("%d[%d]→",p->adjvex,p->weight);
p=p->nextarc;
}
printf("^\n");
}
}
void DestroyAdj(AdjGraph *&G)
{
int i;
ArcNode *pre,*p;
for(i=0;i<G->n;i++) //扫描所有单链表
{
pre=G->adjlist[i].firstarc; //p指向第i个单链表的头结点
if(pre!=NULL)
{
p=pre->nextarc;
while(p!=NULL) //释放第i个单链表的所有结点
{
free(pre);
pre=p;
p=p->nextarc;
}
free(pre);
}
}
free(G);
}
//深度优先遍历
int visited[MAXV]; //全局数组,记录访问的点
void DFS(AdjGraph *G,int v)
{
/********** Begin **********/
visited[v] = 1;
printf("%d ", v);
ANode *p = G -> adjlist[v].firstarc;
while(p){
if(!visited[p -> adjvex]){
DFS(G, p -> adjvex);
p = p -> nextarc;
}
else{
p = p -> nextarc;
continue;
}
}
/********** End **********/
}
第2关:实现图的广度优先遍历
任务描述
本关任务:实现 graph.cpp 里的函数void BFS(AdjGraph *G,int v);
,实现图的广度优先遍历。 注意遵守约定:始终从编号小的开始遍历。
相关知识
广度优先遍历的过程是首先访问初始点 v ,接着访问顶点 v 的所有未被访问过的邻接 v1,v2,•••,vt , 然后再按照 v1,v2,•••,vt 的次序访问每个顶点的所有未被访问过的邻接点,依此类推,直到图中所有和初始点 v 有路径相通的顶点都被访问过为止。 例如,对于图 1 所示的有向图, 从顶点 0 开始进行广度优先遍历,可得到以下访问序列 0 1 3 2 4 , 0 3 1 2 4。
以邻接表为存储结构,在用广度优先遍历图时需要使用一个队列,这里采用环形队列,以类似于 叉树的层次遍历方式来遍历图。
图1
下面以图 1 为例调用 BFS(G,0)
算法,其对应的邻接表如下:
图2
(1). 访问顶点 0 , 0 进队,转 (2) ; (2). 第 1 次循环:顶点 0出队,找其第一个相邻的顶点 1 ,它未被访问过,访问之并将 1 进队;找顶点 0 的下一个邻接点 3 ,它未被访问,访问之并将 3 进队,转 (3); (3). 第 2 次循环:顶点 1 出队,找其第一个相邻的结点 2 ,它未被访问过,访问之并将 2 进队,转 (4); (4). 顶点 3 出队,找 2 ,已被访问过,转 (5);
(5). 顶点 2 出队,找顶点 4 ,它未被访问,访问之,并将 4 进队 转 (6) ; (6). 顶点 4 出队, 无相邻结点,转 (7); (7). 队列为空,遍历结束,即 0 1 3 2 4。
图采用邻接表的方式存储,结构体如下:
#define MAXV 50
#define INF 32767
typedef struct ANode
{
int adjvex; //该边的邻接点编号
struct ANode *nextarc; //指向下一条边的指针
int weight; //该边的相关信息,如权值(这里用整型表示)
}ArcNode; //边结点类型
typedef struct Vnode
{
//InfoType info //顶点的其他信息
ArcNode *firstarc; //指向第一个边结点
}VNode;
typedef struct
{
VNode adjlist[MAXV]; //邻接表的头结点数组
int n,e; //图中的顶点数n和边数e
}AdjGraph ; //完整的图邻接表类型
编程要求
本关任务是实现 graph.cpp 里的函数void BFS(AdjGraph *G,int v);
以完成邻接表为存储结构的广度优先遍历算法,其中 v 是初始点。 注意遵守约定:始终从编号小的开始遍历。 测试文件如下: main.cpp
:
#include "graph.h"
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j,n,e;
int A[MAXV][MAXV];
scanf("%d %d",&n,&e);
AdjGraph *G;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&A[i][j]);
}
}
CreateAdj(G,A,n,e);
BFS(G,0);
printf("\n");
DestroyAdj(G);
return 0;
}
测试说明
本关的测试过程如下:
- 平台编译 step4/main.cpp ;
- 平台运行该可执行文件,并以标准输入方式提供测试输入;
- 平台获取该可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。
输入格式: 输入n e,顶点数 边数 输入一个n×n的一个矩阵表示一个图的邻接矩阵,在带权有向图中,用 32767 代替无穷大。
输出格式: 以顶点开始的广度优先遍历序列。
以下是平台对 step3/main.cpp
的测试样例: 样例输入
5 5
0 8 32767 5 32767
32767 0 3 32767 32767
32767 32767 0 32767 6
32767 32767 9 0 32767
32767 32767 32767 32767 0
样例输出 0 1 3 2 4
参考资料
开始你的任务吧,祝你成功!
最后通关代码:
#include "graph.h"
#include <stdio.h>
#include <stdlib.h>
void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e) //创建图的邻接表
{
int i,j;
ArcNode *p;
G=(AdjGraph *)malloc(sizeof(AdjGraph));
for(i=0;i<n;i++) //给邻接表的头元素的指针域置初值
{
G->adjlist[i].firstarc=NULL;
}
for(i=0;i<n;i++) //检查邻接表的每个元素
{
for(j=n-1;j>=0;j--)
{
if(A[i][j]!=0&&A[i][j]!=INF) //存在一条边
{
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
p->adjvex=j; //存放邻接点
p->weight=A[i][j]; //存放权
p->nextarc=G->adjlist[i].firstarc; //采用头插法插入结点p
G->adjlist[i].firstarc=p;
}
}
}
G->n=n;
G->e=e;
}
void DispAdj(AdjGraph *G)
{
int i;
ArcNode *p;
for(i=0;i<G->n;i++)
{
p=G->adjlist[i].firstarc;
printf("%3d: ",i);
while(p!=NULL)
{
printf("%d[%d]→",p->adjvex,p->weight);
p=p->nextarc;
}
printf("^\n");
}
}
void DestroyAdj(AdjGraph *&G)
{
int i;
ArcNode *pre,*p;
for(i=0;i<G->n;i++) //扫描所有单链表
{
pre=G->adjlist[i].firstarc; //p指向第i个单链表的头结点
if(pre!=NULL)
{
p=pre->nextarc;
while(p!=NULL) //释放第i个单链表的所有结点
{
free(pre);
pre=p;
p=p->nextarc;
}
free(pre);
}
}
free(G);
}
//广度优先遍历
void BFS(AdjGraph *G,int v)
{
/********** Begin **********/
ArcNode *p;
int w,i;
int queue[MAXV],front=0,rear=0;
int visited[MAXV];
for(i=0;i<G->n;i++) visited[i]=0;
printf("%2d",v);
visited[v]=1;
rear=(rear+1)%MAXV;
queue[rear]=v;
while(front!=rear){
front=(front+1)%MAXV;
w=queue[front];
p=G->adjlist[w].firstarc;
while(p!=NULL)
{
if(visited[p->adjvex]==0)
{
printf("%2d",p->adjvex);
visited[p->adjvex]=1;
rear=(rear+1)%MAXV;
queue[rear]=p->adjvex;
}
p=p->nextarc;
}
}
printf("\n");
/********** End **********/
}
第3关:邻接矩阵转邻接表
本关任务要求实现ALGraph ChangeToTable(NMGraph &graph,ALGraph &graph2)函数,来完成邻接矩阵到邻接表的转换。
最后通关代码:
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
#define ElemType int
#define VertexType int
typedef struct{//邻接矩阵存储
ElemType Vertices[MaxVertexNum]; //顶点信息的数组
ElemType Edge[MaxVertexNum][MaxVertexNum]; //边信息的数组
ElemType vexnum; //顶点数
ElemType arcnum;//边数
}NMGraph;
typedef struct ArcNode{//边表结点
int adjvex;
struct ArcNode *next;
//InfoType info; //网的边权值
}ArcNode;
typedef struct VNode{//顶点表结点
VertexType data; //顶点信息
ArcNode *first;
}VMode,AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; //邻接表
int vexnum,arcnum;; //顶点数和边数
}ALGraph;
void CreateGraph(int vnum,int anum,NMGraph &graph,ALGraph &graph2){
//邻接矩阵
graph.vexnum=vnum;
graph.arcnum=anum;
//邻接表
graph2.vexnum=vnum;
graph2.arcnum=anum;
for(int i=0;i<vnum;i++){//图的初始化
for(int j=0;j<vnum;j++){
graph.Edge[i][j]=0;
}
}//for
for(int i=0;i<vnum;i++){//存储顶点信息
int value;
scanf("%d",&value);
//邻接矩阵
graph.Vertices[i]=value;
//邻接表
graph2.vertices[i].data=value;
graph2.vertices[i].first=NULL;
}
printf("\n");
for(int i=0;i<anum;i++){//存储边表信息
int v1,v2,w;
scanf("%d%d%d",&v1,&v2,&w);
//无向图
graph.Edge[v1-1][v2-1]=w;
graph.Edge[v2-1][v1-1]=w;
}
}
ALGraph ChangeToTable(NMGraph &graph,ALGraph &graph2){
/********** Begin **********/
for(int i=0;i<graph2.vexnum;i++){
ArcNode *head=(ArcNode*)malloc(sizeof(ArcNode));
ArcNode *p=head;
p->next=NULL;
for(int j=0;j<graph.vexnum;j++){
if(graph.Edge[i][j] !=0){
ArcNode *edgeNode=(ArcNode*)malloc(sizeof(ArcNode));
edgeNode->adjvex=(j+1);
edgeNode->next=NULL;
p->next=edgeNode;
p=p->next;
}
}
graph2.vertices[i].first=head->next;
head->next=NULL;
}
/********** End **********/
}
//打印相邻结点的方法
void display(ALGraph &graph){
for(int i=0;i<(graph.vexnum);i++){//顶点
ArcNode *p=graph.vertices[i].first;
printf("与顶点%d相邻的顶点为:",graph.vertices[i]);
while(p){
printf("%d ",p->adjvex);
p=p->next;
}
printf("\n");
}
}
//打印邻接矩阵的方法
void display2(NMGraph &graph){
printf(" ");
for(int i=0;i<(graph.vexnum);i++){
printf("%d ",graph.Vertices[i]);
}
printf("\n");
for(int i=0;i<(graph.vexnum);i++){//顶点
printf("%d ",graph.Vertices[i]);
for(int j=0;j<(graph.vexnum);j++){//边
printf("%d ",graph.Edge[i][j]);
}
printf("\n");
}
printf("\n");
}
int main(){
int vnum,anum;
scanf("%d%d",&vnum,&anum);
NMGraph graph;
ALGraph graph2;
CreateGraph(vnum,anum,graph,graph2);
printf("NMGraph邻接矩阵为\n");
display2(graph);
printf("\n邻接矩阵转邻接表\n");
ChangeToTable(graph,graph2);
printf("\n邻边表为\n");
display(graph2);
return 0;
}