实验题目描述
1.基本要求
① 很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示在连通的无向图上访问全部结点的操作。
② 以邻接多重表为存储结构,实现连通元向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
③ 自测数据:教科书图7.33 暂时忽略里程,起点为北京。
④ 设图的结点不超过30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,···,n)。通过输入图的全部边输入一个图,每个边为一个数对,可以对边的输入顺序作出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。
2.参考
利用教科书《数据结构》(紫书)图7.33的数据。
实验过程
1.开发环境描述
1.开发环境:Visual Studio 2019
2.操作系统:win10
2.题目分析及解题思路
- 问题抽象:
图的遍历是图中非常基础的应用,学习图的遍历可以参考树的先根遍历和按层次遍历,深度优先搜索过程是树的先根遍历的推广,广度优先搜索过程是树的按层次遍历的推广。 - 问题解决思路:
①应用深度优先DFS的算法:假设初始状态是图中所有顶点未曾被问,则深度优先搜索可从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
按照本人的理解,深度遍历适合使用栈实现,过程大致是根节点v入栈,输出v并获得v的其中一个邻接点w1,该邻接点W1入栈,重复上述过程,直到Wi无邻接点,Wi出栈,并寻找Wi-1的邻接点,若邻接点存在,重复上述过程,若邻接点不存在,则继续出栈,直到找到某个点有邻接点为止,重复上述过程。用这种方式可以输出无向连通图的所有结点。
通过对BFS的分析过程,我们可以知道深度优先算法有极强的递归特性,因此考虑使用递归完成深度优先算法。
②应用广度优先BFS的算法:假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,就后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上选过程,直至图中所有顶点都被访问到为止。
按照本人的理解,广度遍历适合使用队列实现,过程大致是根节点v入队并输出,然后v出队,得到v所有的邻接点W1,W2…Wn,W1,W2…Wn入队并输出,然后W1出队并得到W1所有邻接点,重复上述过程,直到所有结点全部输出。总而言之,就是入队输出,出队则得邻接点,用这种方式可以输出无向连通图的所有结点。
3.实验代码
①处理交互的Interaction.h文件
#ifndef INTERACTION_H
#define INTERACTION_H
int Get_int();
void Initialize();
int Intercommand();
void Is_refresh();
#endif // ! INTERACTION_H
处理交互的Interaction.cpp文件
#include"interaction.h"
#include<iostream>
#include<stdio.h>
#include<conio.h>
using namespace std;
#define backColor 7
#define textColor 1
int Get_int()
{
int num = 0, i = 0;
char ch[11] = { 0 };
label1:
num = 0;
scanf_s("%s", ch, 10);
i = 0;
if (ch[0] == 45 || ch[0] == 43)
{
i++;
}
while (ch[i])
{
if (ch[i] < '0' || ch[i]>'9')
{
cout << "输入错误,请输入数字:" << endl;
goto label1;
}
else {
num = num * 10 + (int)ch[i] - 48;
}
i++;
}
if (ch[0] == 45)
{
return -num;
}
else
{
return num;
}
}
void Initialize()
{
system("cls");
//设置背景颜色
char command[9] = "color 07"; //默认颜色
command[6] = '0' + backColor; //将backColor变量改为字符型(背景颜色设置为7,为白色)
command[7] = '0' + textColor; //将textColor变量改为字符型 (背景颜色设置为1,为蓝色)
system(command); //调用系统函数
printf(" ***********交通网的深度遍历和广度遍历***********\n");
printf(" | \t0. 退出 |\n");
printf(" | \t1. 直接通过文件读入地图 |\n");
printf(" | \t2. 手动输入地图信息 |\n");
printf(" ***********************************************\n");
}
int Intercommand()
{
cout << "请输入您的选择:";
int input;
while (1)
{
int n = Get_int();
if (n >= 0 && n < 3)
{
input = n;
break;
}
cout << "输入错误,请重输:";
}
return input;
}
void Is_refresh()
{
printf("\n");
cout << "----------------------------------------------------" << endl;
cout << "输入任意字符完成查看,回到菜单页面:" << endl;
_getch();
}
② 处理队列操作的myquene.h
#ifndef MYQUENE_H
#define MYQUENE_H
typedef struct QENode
{
int data;
struct QENode* next;
}QENode; //队列结点
typedef struct
{
QENode* rear;
QENode* front;
}LinkQueue; //队列
void InitQueue(LinkQueue* Q); //初始化队列
void QueueAppend(LinkQueue* Q, int v); //增加队列元素
void QueueDelete(LinkQueue* Q, int* v); //减少队列元素
#endif
处理队列操作的myquene.cpp
#include"myquene.h"
#include"graph.h"
#include<iostream>
#include<stdlib.h>
using namespace std;
void InitQueue(LinkQueue* Q) //队列的初始化
{
Q->front = Q->rear = (QENode*)malloc(sizeof(QENode));
Q->front->next = NULL;
}
void QueueAppend(LinkQueue* Q, int v)//入队列
{
QENode* p;
p = (QENode*)malloc(sizeof(QENode));
p->data = v;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
}
void QueueDelete(LinkQueue* Q, int* v)//出队列
{
QENode* p;
if (Q->front == Q->rear)
return;
p = Q->front->next;
*v = p->data;
Q->front->next = p->next;
if (Q->rear == p)
Q->rear = Q->front;
free(p);
}
③处理图的处理的graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include <string>
using namespace std;
#define MAX 30
//边结点
typedef struct ENode
{
int ivex, jvex; //该边依附的两个顶点在数组中的序号
struct ENode* ilink; //指向下一条依附于顶点ivex的边
struct ENode* jlink; //指向下一条依附于顶点jvex的边
}ENode;
//顶点结点
typedef struct VNode
{
int mark; //标记
string data; //顶点信息
int number; //顶点编号
ENode* firstedge;
}VNode;
//图
typedef struct
{
VNode AMlist[MAX]; //结点信息
int v_num; //顶点数
int e_num; //边数
}Graph;
void Initilized(Graph* graph); //初始化图
void CreateGraph(Graph* graph); //创建图
void SetMark(Graph* graph); //设置标记
void DFS(Graph* graph, int v); //深度遍历
void BFS(Graph* graph, int u); //广度遍历
void CreateGraph_by_file(Graph* graph);//通过文件创建图
#endif // !GRAPH_H
处理图的处理的graph.cpp
#include"graph.h"
#include"myquene.h"
#include<iostream>
#include"interaction.h"
#include<fstream>
#include<string>
using namespace std;
void Initilized(Graph* graph)//图的初始化
{
graph = (Graph*)malloc(sizeof(Graph));
graph->v_num = 0;
graph->e_num = 0;
}
int graph_flag = 0;
void CreateGraph(Graph* graph)//图的创建图
{
ENode* p, * q, * e;
e = NULL;
int i,input_flag=0;
printf("请输入连通无向图的顶点数 :");
graph->v_num = Get_int();
do{
if (input_flag++ > 1)
printf("输入错误,请重新输入\n");
printf("请输入连通无向图的边数 :");
graph->e_num = Get_int();
} while (graph->e_num < graph->v_num - 1);
for (i = 1; i <= graph->v_num; i++)
{
printf("请输入第%d个顶点的信息:\n", i);
cin >> graph->AMlist[i].data;
graph->AMlist[i].number = i;
graph->AMlist[i].firstedge = NULL;
graph->AMlist[i].mark = 0;
}
//输入每个点的信息
for (i = 1; i <= graph->e_num; i++)
{
p = (ENode*)malloc(sizeof(ENode));
printf("请输入第%d条边的首尾两个点(编号小的在前 (例如1 3回车))\n",i);
while (1)
{
p->ivex = Get_int();
p->jvex = Get_int();
if (p->ivex<p->jvex && p->ivex>0 && p->jvex <= graph->v_num && p->ivex!=p->jvex)
break;
else {
cout << "您输入错误,请重新输入" << endl;
}
}
p->ilink = p->jlink = NULL; //边信息填入完毕
/*将边连在一起*/
if (graph->AMlist[p->ivex].firstedge == NULL)
graph->AMlist[p->ivex].firstedge = p; //邻接多重表的顶点首指针指向p
else
{
q = graph->AMlist[p->ivex].firstedge; //记录首边
while (q != NULL)
{
e = q;
if (q->ivex == p->ivex)
q = q->ilink;
else
q = q->jlink;
}
if (e->ivex == p->ivex)
e->ilink = p;
else
e->jlink = p;
}
if (graph->AMlist[p->jvex].firstedge == NULL)
graph->AMlist[p->jvex].firstedge = p;
else
{
q = graph->AMlist[p->jvex].firstedge;
while (q != NULL)
{
e = q;
if (q->ivex == p->ivex)
q = q->ilink;
else
q = q->jlink;
}
if (e->ivex == p->ivex)
e->ilink = p;
else
e->jlink = p;
}
}
}
void SetMark(Graph* graph)//设置访问标记
{
int i;
for (i = 1; i <= graph->v_num; i++)
graph->AMlist[i].mark = 0;
}
void DFS(Graph* graph, int v)//深度遍历
{
ENode* p;
cout << graph->AMlist[v].data <<" "; graph_flag++;
graph->AMlist[v].mark = 1;
p = graph->AMlist[v].firstedge;
while (p != NULL)
{
if (p->ivex == v)
{
if (graph->AMlist[p->jvex].mark == 0)
{
cout << "<" << p->ivex << "," << p->jvex << ">" << endl;
DFS(graph, p->jvex);
}
p = p->ilink;
}
else
{
if (graph->AMlist[p->ivex].mark == 0)
{
cout << "<" << p->jvex << "," << p->ivex << ">" << endl;
DFS(graph, p->ivex);
}
p = p->jlink;
}
}
}
void BFS(Graph* graph, int u)//广度遍历
{
LinkQueue Q;
ENode* p;
InitQueue(&Q);
cout << graph->AMlist[u].data << endl;
graph->AMlist[u].mark = 1;
QueueAppend(&Q, u);
while (Q.front != Q.rear)
{
QueueDelete(&Q, &u);
p = graph->AMlist[u].firstedge;
while (p != NULL)
{
if (p->ivex == u)
{
if (graph->AMlist[p->jvex].mark == 0)
{
QueueAppend(&Q, p->jvex);
graph->AMlist[p->jvex].mark = 1;
cout << graph->AMlist[p->jvex].data << endl;
}
p = p->ilink;
}
else
{
if (graph->AMlist[p->ivex].mark == 0)
{
QueueAppend(&Q, p->ivex);
graph->AMlist[p->ivex].mark = 1;
cout << graph->AMlist[p->ivex].data << endl;
}
p = p->jlink;
}
}
}
}
void CreateGraph_by_file(Graph* graph)
{
fstream fp;
fp.open("graph_info.txt", ios::in);
fp >> graph->v_num; //cout << graph->v_num;
fp.seekg(1, ios::cur);
fp >> graph->e_num; //cout << graph->e_num;
fp.seekg(2, ios::cur);
//cout << graph->v_num << " " << graph->e_num << endl;
char v_info[10];
int i = 0;
while (++i <= graph->v_num && fp.getline(v_info, 100,'\n') ) // line中不包括每行的换行符
{
graph->AMlist[i].number = i;
graph->AMlist[i].firstedge = NULL;
graph->AMlist[i].mark = 0;
graph->AMlist[i].data=v_info;
//cout << graph->AMlist[i].data<<endl;
}
ENode* p, * q, * e;
e = (ENode*)malloc(sizeof(ENode));
q = (ENode*)malloc(sizeof(ENode));
for (i = 1; i <= graph->e_num; i++)
{
p = (ENode*)malloc(sizeof(ENode));
fp >> p->ivex; //cout << p->ivex<<endl;
fp.seekg(1, ios::cur);
fp >> p->jvex; //cout << p->jvex<<endl;
fp.seekg(2, ios::cur);
p->ilink = p->jlink = NULL; //边信息填入完毕
/*将边连在一起*/
if (graph->AMlist[p->ivex].firstedge == NULL)
graph->AMlist[p->ivex].firstedge = p; //邻接多重表的顶点首指针指向p
else
{
q = graph->AMlist[p->ivex].firstedge; //记录首边
while (q != NULL)
{
e = q;
if (q->ivex == p->ivex)
q = q->ilink;
else
q = q->jlink;
}
if (e->ivex == p->ivex)
e->ilink = p;
else
e->jlink = p;
}
if (graph->AMlist[p->jvex].firstedge == NULL)
graph->AMlist[p->jvex].firstedge = p;
else
{
q = graph->AMlist[p->jvex].firstedge;
while (q != NULL)
{
e = q;
if (q->ivex == p->ivex)
q = q->ilink;
else
q = q->jlink;
}
if (e->ivex == p->ivex)
e->ilink = p;
else
e->jlink = p;
}
}
fp.close();
}
④源.cpp
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include"graph.h"
#include"myquene.h"
#include"interaction.h"
using namespace std;
int main()
{
int v1, v2; //定义两个顶点
Graph graph; //定义了一个图
int choose;
while (1)
{
Initialize();
fflush(stdin);
choose = Intercommand();
switch (choose)
{
case 0:
exit(0);
case 2:
Initilized(&graph);
CreateGraph(&graph);
printf("\n输入深度广度遍历的起始点:\n");
v2 = Get_int();
v1 = v2;
printf("\n深度遍历序列:\n顶点序列:\n");
SetMark(&graph);
DFS(&graph, v2);
printf("\n广度遍历序列:\n顶点序列:\n");
SetMark(&graph);
BFS(&graph, v1);
Is_refresh();
case 1:
Initilized(&graph);
CreateGraph_by_file(&graph);
fflush(stdin);
printf("\n输入深度广度遍历的起始点:\n");
v2 = Get_int();
v1 = v2;
printf("\n深度遍历序列:\n");
SetMark(&graph);
DFS(&graph, v2);
printf("\n广度遍历序列:\n");
SetMark(&graph);
BFS(&graph, v1);
Is_refresh();
}
}
return 0;
}
4.文件
- graph-info.txt内容
25 30
Urumchi
Sining
Lanzhou
Hohhot
BeiJing
Tianjin
Shenyang
Changchun
Harbin
Dalian
Xian
Zhengzhou
Xuzhou
ChengDu
Wuhan
Shanghai
Kunming
Guiyang
Zhuzhou
Nanchang
Fuzhou
Nanning
Liuzhou
Guangzhou
Shenzhen
1 3
2 3
3 4
3 11
11 12
4 5
5 12
12 13
5 6
6 13
6 7
7 10
7 8
8 9
11 14
14 17
14 18
17 18
12 15
22 23
18 23
18 19
19 23
19 24
24 25
19 20
20 21
16 20
13 16
15 19
联系我
如果对代码有改进想法的或者需要源代码的,可联1274669808@qq.com