6-1 邻接表存储图的广度优先遍历
分数 20
全屏浏览题目
切换布局
作者 DS课程组
单位 浙江大学
试实现邻接表存储图的广度优先遍历。
函数接口定义:
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) );
其中LGraph
是邻接表存储的图,定义如下:
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV; /* 邻接点下标 */
PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};
/* 顶点表头结点的定义 */
typedef struct Vnode{
PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum]; /* AdjList是邻接表类型 */
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
函数BFS
应从第S
个顶点出发对邻接表存储的图Graph
进行广度优先搜索,遍历时用裁判定义的函数Visit
访问每个顶点。当访问邻接点时,要求按邻接表顺序访问。题目保证S
是图中的合法顶点。
裁判测试程序样例:
#include <stdio.h>
typedef enum {false, true} bool;
#define MaxVertexNum 10 /* 最大顶点数设为10 */
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV; /* 邻接点下标 */
PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};
/* 顶点表头结点的定义 */
typedef struct Vnode{
PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum]; /* AdjList是邻接表类型 */
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
bool Visited[MaxVertexNum]; /* 顶点的访问标记 */
LGraph CreateGraph(); /* 创建图并且将Visited初始化为false;裁判实现,细节不表 */
void Visit( Vertex V )
{
printf(" %d", V);
}
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) );
int main()
{
LGraph G;
Vertex S;
G = CreateGraph();
scanf("%d", &S);
printf("BFS from %d:", S);
BFS(G, S, Visit);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:给定图如下
2
输出样例:
BFS from 2: 2 0 3 5 4 1 6
我的第一种方法(错):
#include <stdio.h>
#include <stdlib.h>
//typedef enum {false, true} bool;
#define MaxVertexNum 10 /* 最大顶点数设为10 */
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode {
Vertex AdjV; /* 邻接点下标 */
PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};
/* 顶点表头结点的定义 */
typedef struct Vnode {
PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum]; /* AdjList是邻接表类型 */
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode {
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef struct {
int L,R;
} bian;
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
bool Visited[MaxVertexNum]; /* 顶点的访问标记 */
LGraph CreateGraph(); /* 创建图并且将Visited初始化为false;裁判实现,细节不表 */
void Visit( Vertex V ) {
printf(" %d", V);
}
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) );
int main() {
LGraph G;
Vertex S;
G = CreateGraph();
for(int i=0; i<7; i++) {
PtrToAdjVNode k;
printf("%d:",i);
k=G->G[i].FirstEdge;
if(k) {
while(k) {
printf(" %d",k->AdjV);
k=k->Next;
}
}
printf("\n");
}
printf("请输入要开始进行遍历的结点:");
scanf("%d", &S);
printf("BFS from %d:", S);
BFS(G, S, Visit);
return 0;
}
/* 你的代码将被嵌在这里 */
//实现遍历-》广度遍历
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) ) {we
int list[1000];
int left,right;
left=right=0;
Visit(S);
Visited[S]=true;
PtrToAdjVNode i;
// 注意G并不是指针,是一个数组类型的,即像int a.
i=Graph->G[S].FirstEdge;
int x;
while(i) {
Visited[i->AdjV]=true;
list[right++]=i->AdjV;
i=i->Next;
}
while(left<right) {
// printf("left:%d,right:%d\n",left,right);
int tran=list[left++];
Visit(tran);//出队
//Visited[tran]=true;
for(PtrToAdjVNode w=Graph->G[tran].FirstEdge; w; w=w->Next) {
if(!Visited[w->AdjV]) {
// 放入队列的时要先进行标记表示已经访问过了
Visited[w->AdjV]=true;
list[right++]=w->AdjV;
}
}
}
}
LGraph CreateGraph() {
LGraph head;
bian *tran;
PtrToAdjVNode lin;
tran=(bian *)malloc(sizeof(bian));
head=(LGraph)malloc(sizeof(struct GNode));
//注意顶点的存储从0开始存储
printf("总的顶点数:");
scanf("%d",&head->Nv);
// 初始化
for(int i=0; i<MaxVertexNum; i++) {
head->G[i].FirstEdge=NULL;
}
printf("请输入边的个数:");
scanf("%d",&head->Ne);
// 存储开始
for(int i=0; i<head->Ne; i++) {
printf("请输入两个顶点:");
scanf("%d%d",&tran->L,&tran->R);
lin=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
// 第一次(无向图)
lin->AdjV=tran->R;
lin->Next=head->G[tran->L].FirstEdge;
head->G[tran->L].FirstEdge=lin;
printf("%d %d\n",tran->L,head->G[tran->L].FirstEdge->AdjV);
// 返回来存
//注意又得申请一组空间,不然进行的操作都在同一组空间中
lin=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
lin->AdjV=tran->L;
lin->Next=head->G[tran->R].FirstEdge;
head->G[tran->R].FirstEdge=lin;
printf("%d %d\n",tran->R,head->G[tran->R].FirstEdge->AdjV);
}
return head;
}
改正+总结思路:
//无论是用什么方式存储图,data域我们一般表示存储的是该节点的名字
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) ) {
// 创建队列进行存储入队结点的名字
int list[1000];
int pre,left;
PtrToAdjVNode tran;
pre=left=0;
Visited[S]=true;//进入队列的就是已经访问过了,要进行标记
list[pre++]=S;
while(left<pre) {
int i=list[left++];//表示出对,取出出队的结点
Visit(i);
// Visited[i]=true;在入队的时候就已经进行标记了
tran=Graph->G[i].FirstEdge;//获取该结点名字在表头的位置的相邻结点 ,注意这里一定是i,不是S,
//如果是S就获得不了 在队列的下一个结点,就只会在S的这个位置不变。
//将该节点的相邻结点进行入对
while(tran){
if(!Visited[tran->AdjV]){//除了要进行检测有没有相邻的结点,还要进行检测有没有访问过该节点
//没有访问就进行入队,同是进行标记
Visited[tran->AdjV]=true;
list[pre++]=tran->AdjV;
}
tran=tran->Next;//这个不能写在if里面,不然tran的位置在不执行if时就会不动
}
}
}
//创建
LGraph CreateGraph() {
LGraph head;
bian *tran;
PtrToAdjVNode lin;
tran=(bian *)malloc(sizeof(bian));
head=(LGraph)malloc(sizeof(struct GNode));
//注意顶点的存储从0开始存储
printf("总的顶点数:");
scanf("%d",&head->Nv);
// 初始化
for(int i=0; i<MaxVertexNum; i++) {
head->G[i].FirstEdge=NULL;
}
printf("请输入边的个数:");
scanf("%d",&head->Ne);
// 存储开始
for(int i=0; i<head->Ne; i++) {
printf("请输入两个顶点:");
scanf("%d%d",&tran->L,&tran->R);
lin=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
// 第一次(无向图)
lin->AdjV=tran->R;
lin->Next=head->G[tran->L].FirstEdge;
head->G[tran->L].FirstEdge=lin;
printf("%d %d\n",tran->L,head->G[tran->L].FirstEdge->AdjV);
// 返回来存
//注意又得申请一组空间,不然进行的操作都在同一组空间中
lin=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
lin->AdjV=tran->L;
lin->Next=head->G[tran->R].FirstEdge;
head->G[tran->R].FirstEdge=lin;
printf("%d %d\n",tran->R,head->G[tran->R].FirstEdge->AdjV);
}
return head;
}
总结:采用第一种方法不能说有错,我问老师后,老师说可能是是这个pta的程序设置有问题,因为两种方法的思路是一样的,只不过我的第一种是将第二种的开始位置入队放在前面而已。