目录
题目描述
本实验实现邻接表表示下无向图的广度优先遍历。
程序的输入是图的顶点序列和边序列(顶点序列以*为结束标志,边序列以-1,-1为结束标志)。程序的输出为图的邻接表和广度优先遍历序列。例如:
程序输入为:
a
b
c
d
e
f
*
0,1
0,4
1,4
1,5
2,3
2,5
3,5
-1,-1
程序的输出为:
the ALGraph is
a 4 1
b 5 4 0
c 5 3
d 5 2
e 1 0
f 3 2 1
the Breadth-First-Seacrh list:aebfdc
问题分析
解决这个问题需要两步。
首先需要完成邻接表建图。然后用建好的图进行广度优先遍历。
都是比较基础的操作,下面我们直接看着代码解释。
问题解决
首先,我们需要用邻接表建图。邻接表包括顶点数组和每个顶点所连的一串边。图解如下
其中,每个顶点的信息包括两部分:所存数据和所连的第一条边指针。
每条边的信息也包括两部分:所连的顶点的下标和指向下一条边的指针。
由此,我们可以定义两个结构体:
struct edge //边的结构体
{
int ver; //所连顶点下标
struct edge* next; //下一条边
};
struct vertex //顶点结构体
{
char data; //顶点数据
struct edge* firstedge; //所连第一条边
};
然后,我们就可以定义整个图的结构体了
struct Graph //图的结构体
{
struct vertex adjlist[MAXNUM]; //顶点组成的数组,MAXNUM为最多顶点数
int vernum=0,edgenum=0; //顶点数,边数
};
接下来,我们就可以开始完成那两个任务了。
首先是建图:
void CreateGraph(struct Graph &G)
{
struct edge *e = nullptr;
int i,j;
char v;
cin>>v;
while(v!='*') //v='*'代表顶点输入完毕
{
G.adjlist[G.vernum].data=v; //给顶点赋值
G.adjlist[G.vernum].firstedge=nullptr; // 初始化顶点所连第一条边
cin>>v;
G.vernum++;
}
scanf("%d,%d",&i,&j);
while(i!=-1&&j!=-1) //顶点下标组都为-1代表输入完毕
{
e=new edge; //无向图要建立两次边,这是第一次
e->ver=j;
e->next=G.adjlist[i].firstedge;
G.adjlist[i].firstedge=e;
//每条边都插在第一条边的位置,所以每个顶点之后所连的边是倒序的
e=new edge; //无向图要建立两次边,这是第二次
e->ver=i;
e->next=G.adjlist[j].firstedge;
G.adjlist[j].firstedge=e;
scanf("%d,%d",&i,&j);
G.edgenum++; //边数++
}
}
然后是BFS:
void BFS(Graph &G,int vt)
{
queue<int> Q; //建队列
edge *e;
Q.push(vt); //起始顶点入队
visited[vt]=1; //访问状态设置为已访问
while(Q.empty()==0) //队列不为空时
{
vt=Q.front(); //队首元素出队
cout<<G.adjlist[vt].data; //输出其数据
Q.pop();
e=G.adjlist[vt].firstedge;
while (e!=nullptr) //顺着它所连接的边遍历
{
if(visited[e->ver]==0) //若其所连边有没访问过的,入队,并更改访问状态
{
visited[e->ver]=1;
Q.push(e->ver);
}
e=e->next;
}
}
for(int i=0;i<G.vernum;i++) //一定要注意!!检查是否还有没访问过的点!!递归BFS
{
if(visited[i]==0)
{
BFS(G,i);
}
}
}
BFS时要注意几点,首先是要记得设置访问状态。(这个访问数组在main函数里记得初始化,一会也会强调)
还有就是要注意有非联通子图的情况!一开始用队列做我没考虑到这点,最后还是用递归拯救了一下。
如果一直WA的话可以尝试一下包含非连通子图和孤立点的测试用例。
最后是main函数:
int main()
{
Graph G;
CreateGraph(G); //建图
cout << "the ALGraph is\n";
for (int i = 0; i <G.vernum; i++) //输出图
{
if(G.adjlist[i].firstedge!=nullptr)//输出注意格式!!很坑!!
{
cout << G.adjlist[i].data << " ";
}
else cout << G.adjlist[i].data;
edge *e = G.adjlist[i].firstedge;
while (e != nullptr)
{ if(e->next!=nullptr)//输出注意格式!!
{
cout << e->ver <<" ";
}
else cout << e->ver;
e = e->next;
}
cout << "\n";
}
for(int i=0;i<G.vernum;i++) //初始化visited数组
{
visited[i]=0;
}
cout << "the Breadth-First-Seacrh list:";
BFS(G, 0); // 从顶点0开始遍历
cout<<endl;
return 0;
}
main函数其实没啥好说的,但是一定要注意格式!!试出来这个错的时候我感觉大为震撼哈哈哈真的很难debug。就是输出图的时候如果是孤立点,后面不要空格,直接换行。还有这条边后面如果没有边了,也直接换行,不要空格。
最后是完整代码:
#include <iostream>
#include <vector>
#include <queue>
#include <cstdlib>
using namespace std;
const int MAXNUM = 1000;
int visited[MAXNUM];
struct edge
{
int ver;
struct edge* next;
};
struct vertex
{
char data;
struct edge* firstedge;
};
struct Graph
{
struct vertex adjlist[MAXNUM];
int vernum=0,edgenum=0;
};
void CreateGraph(struct Graph &G)
{
struct edge *e = nullptr;
int i,j;
char v;
cin>>v;
while(v!='*')
{
G.adjlist[G.vernum].data=v;
G.adjlist[G.vernum].firstedge=nullptr;
cin>>v;
G.vernum++;
}
scanf("%d,%d",&i,&j);
while(i!=-1&&j!=-1)
{
e=new edge;
e->ver=j;
e->next=G.adjlist[i].firstedge;
G.adjlist[i].firstedge=e;
e=new edge;
e->ver=i;
e->next=G.adjlist[j].firstedge;
G.adjlist[j].firstedge=e;
scanf("%d,%d",&i,&j);
G.edgenum++;
}
}
void BFS(Graph &G,int vt)
{
queue<int> Q;
edge *e;
Q.push(vt);
visited[vt]=1;
while(Q.empty()==0)
{
vt=Q.front();
cout<<G.adjlist[vt].data;
Q.pop();
e=G.adjlist[vt].firstedge;
while (e!=nullptr)
{
if(visited[e->ver]==0)
{
visited[e->ver]=1;
Q.push(e->ver);
}
e=e->next;
}
}
for(int i=0;i<G.vernum;i++)
{
if(visited[i]==0)
{
BFS(G,i);
}
}
}
int main()
{
Graph G;
CreateGraph(G);
cout << "the ALGraph is\n";
for (int i = 0; i <G.vernum; i++)
{
if(G.adjlist[i].firstedge!=nullptr)//格式
{
cout << G.adjlist[i].data << " ";
}
else cout << G.adjlist[i].data;
edge *e = G.adjlist[i].firstedge;
while (e != nullptr)
{ if(e->next!=nullptr)//格式
{
cout << e->ver <<" ";
}
else cout << e->ver;
e = e->next;
}
cout << "\n";
}
for(int i=0;i<G.vernum;i++)
{
visited[i]=0;
}
cout << "the Breadth-First-Seacrh list:";
BFS(G, 0); // 从顶点0开始遍历
cout<<endl;
return 0;
}
反思总结
对c++基础语法不太熟,做这道题的时候学到了几点:
1、c++声明结构体变量的时候不写struct也可以(当然定义的时候一定要写)
2、c++的空指针在c++11里推荐用nullptr,不推荐用NULL
3、有‘,’隔开两个输入数据的时候最简单可以用c里的scanf(“%x,%x”,&x,&x);
也可以用cin>>i; cin.ignore(1,','); cin>>j; //cin.ignore(1,',')的意思是最多跳过一个字符,如果是‘,’就跳过,其他不跳过。