1、实验目的:
1、熟悉图遍历的两种方法:深度优先与广度优先;
2、掌握编程实现图遍历具体算法;
3、掌握图的概念和结构特征,以及各种存储结构的特点及适用范围;
4、了解最小生成树有关算法。
二、实验内容:
选题1: 校园导游程序
用无向网表示你所在学校的校园景点的平面图,图中顶点表示主要景点如图书馆、教学楼、实验楼、办公楼、学生活动中心和学生宿舍楼等,顶点的Info域存放景点的编号、名称、简介等信息,图中的边表示景点间的道路,存放路径长度等信息。
要求实现以下功能:
1、查询各景点的相关信息;
2、查询图中任意两个景点间的最短路径。
选题2: 无向图的遍历程序
以邻接表为存储结构,以递归形式实现无向连通图的深度优先遍历。以用户指定的结点为起点,输出深度优先遍历下的结点访问序列和相应生成树的边集。
三、数据类型定义及功能模块声明
选题1: 校园导游程序
#define MaxInt 32767
#define MVNum 100
typedef struct message
{
int num; //景点代码
char name[100]; //景点名称
char pro[500]; //简介
} Ciceroni;
typedef Ciceroni VerTexType;
typedef int ArcType;
typedef struct
{
VerTexType vexs[MVNum];
ArcType arcs[MVNum][MVNum];
int vexnum, arcnum;
} AMGraph;
选题2: 无向图的遍历程序
#define MAX_VERTEX_NUM 30 //图的最大顶点数不超过30个
#define MAXQSIZE 40 //队列的最大容量
typedef struct ArcNode
{
int adjvex; //顶点位置
struct ArcNode* nextarc; //指向下一条弧的指针
}ArcNode; //弧结点
typedef struct {
ArcNode* AdjList[MAX_VERTEX_NUM]; //指向第一条依附该顶点的弧的指针
int vexnum, arcnum; //图的当前顶点和弧数
}Graph;
typedef struct //队列结构
{
int elem[MAXQSIZE]; //数据域
int front; // 队头指针
int rear; // 队尾指针
}SqQueue;
四、主要算法
选题1: 校园导游程序
//地图的创建
void CreatMap(AMGraph& G)
{
int i, j;
G.vexnum = 10;
G.arcnum = 12;
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
G.arcs[i][j] = MaxInt;
//已知路线长度
G.arcs[0][1] = 150;
G.arcs[0][2] = 100;
G.arcs[1][9] = 200;
G.arcs[2][3] = 200;
G.arcs[2][9] = 300;
G.arcs[2][4] = 300;
G.arcs[3][6] = 200;
G.arcs[4][5] = 100;
G.arcs[4][7] = 50;
G.arcs[5][6] = 100;
G.arcs[7][8] = 50;
G.arcs[8][9] = 100;
for (i = 0; i < 10; i++)
G.vexs[i] = school[i];
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
G.arcs[j][i] = G.arcs[i][j];
}
//佛洛伊德算法求vi,vj之间最短距离
void ShortestPath_Floyd(AMGraph G)
{
int i, j, k, D[MVNum][MVNum], path[MVNum][MVNum];
int place[MVNum];
int v0, v1;
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
{
D[i][j] = G.arcs[i][j];
if (D[i][j] < MaxInt && i != j)
path[i][j] = i;//j前驱为i
else
path[i][j] = -1;//j前驱为-1
}
for (k = 0; k < G.vexnum; k++)
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
if (D[i][k] + D[k][j] < D[i][j])//从i到k,再到j路径更短
{
D[i][j] = D[i][k] + D[k][j];
path[i][j] = path[k][j];
}
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
if (i == j)
D[i][j] = D[j][i] = 0;
cout << "请输入起点:";
cin >> v0;
cout << "请输入终点:";
cin >> v1;
v0--;
v1--;//下标从0开始
int t = v1;//v1接下来会改变,寄存给t
i = 0;
cout << "【" << G.vexs[v0].name << "】到【" << G.vexs[v1].name << "】的最短距离为 " << D[v0][v1] << " m\n";
cout << "\n其最短路线为 :【" << G.vexs[v0].name << "】<->";
while (path[v0][v1] != v0)
{
place[i] = path[v0][v1];
i++;
v1 = path[v0][v1];
}
i--;
for (i; i >= 0; i--)
cout << "【" << G.vexs[place[i]].name << "】<->";
cout << "【" << G.vexs[t].name << "】" << endl;
}
选题2: 无向图的遍历程序
void DFSTraverse(Graph G)
{
int i;
cout << "深度优先遍历结果为:" << endl;
for (i = 1; i <= G.vexnum; i++)
visited[i] = false; //访问标志数组初始化
for (i = 1; i <= G.vexnum; i++)
if (!visited[i]) DFS(G, i);
cout << endl;
}
//从第i个顶点出发递归地深度遍历图G
void DFS(Graph G, int i) {
int w;
visited[i] = true; //访问第i个顶点
cout << i << " ";
for (w = firstAdjVex(G, i); w > 0; w = NextAdjVex(G, i, w))
if (!visited[w]) DFS(G, w); //对尚未访问的邻接顶点w调用DFS
}
//在图G中寻找第v个顶点的第一个邻接顶点
int firstAdjVex(Graph G, int v)
{
if (!G.AdjList[v])
return 0;
else
return(G.AdjList[v]->adjvex);
}
//在图中寻找第v个顶点的相对于u的下一个邻接顶点
int NextAdjVex(Graph G, int v, int u)
{
ArcNode* p;
p = G.AdjList[v];
while (p->adjvex != u) p = p->nextarc; //在顶点v的弧链中找到顶点u
if (p->nextarc == NULL)
return 0; //若已经是最后一个结点,返回0
else
return(p->nextarc->adjvex); //返回下一个邻接顶点的序号
}
五、测试数据及运行结果
选题1: 校园导游程序
选题2: 无向图的遍历程序
六、源代码
选题1: 校园导游程序
#include <iostream>
using namespace std;
#define MaxInt 32767
#define MVNum 100
typedef struct message
{
int num; //景点代码
char name[100]; //景点名称
char pro[500]; //简介
} Ciceroni;
typedef Ciceroni VerTexType;
typedef int ArcType;
typedef struct
{
VerTexType vexs[MVNum];
ArcType arcs[MVNum][MVNum];
int vexnum, arcnum;
} AMGraph;
//存储地点基本信息
Ciceroni school[20] = { {1,"北苑13栋","修仙者永不关灯"},{2,"食堂","干饭人的天堂"},{3,"博雅楼","考研人永不言败"},
{4,"西操场","足球网又该换了"},{5,"明德楼","早八噩梦之地"},{6,"求真楼","打疫苗,来根针吧"},{7,"图书馆","好久没去了,忘记里面啥样了"},
{8,"广学楼","新装修就是不一样"},{9,"慎思楼","它的六楼好难爬"},{10,"博文楼","博雅楼我兄弟"}
};
//地图的创建
void CreatMap(AMGraph& G)
{
int i, j;
G.vexnum = 10;
G.arcnum = 12;
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
G.arcs[i][j] = MaxInt;
//已知路线长度
G.arcs[0][1] = 150;
G.arcs[0][2] = 100;
G.arcs[1][9] = 200;
G.arcs[2][3] = 200;
G.arcs[2][9] = 300;
G.arcs[2][4] = 300;
G.arcs[3][6] = 200;
G.arcs[4][5] = 100;
G.arcs[4][7] = 50;
G.arcs[5][6] = 100;
G.arcs[7][8] = 50;
G.arcs[8][9] = 100;
for (i = 0; i < 10; i++)
G.vexs[i] = school[i];
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
G.arcs[j][i] = G.arcs[i][j];
}
void PrintMap()
{
cout << "\n";
cout << " 【学校地图】\n";
cout << "食堂(2)--------北苑13栋(1) \n";
cout << " | | \n";
cout << "博文楼(10)------博雅楼(3)------------ 西操场(4) \n";
cout << " | | | \n";
cout << "慎思楼(9)-广学楼(8)-明德楼(5)-求真楼(6)-图书馆(7)\n\n";
}
void PrintPlace(AMGraph G, int num)
{
cout << "【景点编码】:" << G.vexs[num - 1].num << "\n【景点名称】:" << G.vexs[num - 1].name << "\n【景点简介】:" << G.vexs[num - 1].pro << endl;
}
void Menu()
{
cout << "┍---------------------------------------┑"<<endl;
cout << " *************校园导游程序】************** " <<endl;
cout << " **********【1】--查看学校平面图**********"<<endl;
cout << " **********【2】--求两景点之间的最短路径**"<<endl;
cout << " **********【3】--查看指定景点信息********"<<endl;
cout << " **********【0】--退出程序****************"<<endl;
cout << "┕----------------------------------------┙"<<endl;
cout << "请输入指令(0-3):";
}
//查找地点
int CheckNum(int num, AMGraph G)
{
for (int i = 0; i < G.vexnum; i++)
if (G.vexs[i].num == num)
return 1;
return 0;
}
//佛洛伊德算法求vi,vj之间最短距离
void ShortestPath_Floyd(AMGraph G)
{
int i, j, k, D[MVNum][MVNum], path[MVNum][MVNum];
int place[MVNum];
int v0, v1;
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
{
D[i][j] = G.arcs[i][j];
if (D[i][j] < MaxInt && i != j)
path[i][j] = i;//j前驱为i
else
path[i][j] = -1;//j前驱为-1
}
for (k = 0; k < G.vexnum; k++)
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
if (D[i][k] + D[k][j] < D[i][j])//从i到k,再到j路径更短
{
D[i][j] = D[i][k] + D[k][j];
path[i][j] = path[k][j];
}
for (i = 0; i < G.vexnum; i++)
for (j = 0; j < G.vexnum; j++)
if (i == j)
D[i][j] = D[j][i] = 0;
cout << "请输入起点:";
cin >> v0;
cout << "请输入终点:";
cin >> v1;
v0--;
v1--;//下标从0开始
int t = v1;//v1接下来会改变,寄存给t
i = 0;
cout << "【" << G.vexs[v0].name << "】到【" << G.vexs[v1].name << "】的最短距离为 " << D[v0][v1] << " m\n";
cout << "\n其最短路线为 :【" << G.vexs[v0].name << "】<->";
while (path[v0][v1] != v0)
{
place[i] = path[v0][v1];
i++;
v1 = path[v0][v1];
}
i--;
for (i; i >= 0; i--)
cout << "【" << G.vexs[place[i]].name << "】<->";
cout << "【" << G.vexs[t].name << "】" << endl;
}
void Order(AMGraph G)
{
while (1)
{
int x, num, visited[MVNum] = { 0 };;
Menu();
cin >> x;
switch (x)
{
case 0:
cout << "成功退出程序...爱你呦...欢迎下次光临"<<endl;
return;
break;
case 1:
PrintMap();
break;
case 2:
ShortestPath_Floyd(G);
break;
case 3:
cout << "请输入查找景点编码:";
cin >> num;
if (!CheckNum(num, G))
cout << "无效编码!" << endl;
else
PrintPlace(G, num);
break;
default:
cout << "无效指令!" << endl;
system("cls");
}
}
}
int main()
{
AMGraph G;
CreatMap(G);
Order(G);
return 0;
}
选题2: 无向图的遍历程序
#include<iostream>
using namespace std;
#define MAX_VERTEX_NUM 30 //图的最大顶点数不超过30个
#define MAXQSIZE 40 //队列的最大容量
enum BOOL { False, True };
typedef struct ArcNode
{
int adjvex; //顶点位置
struct ArcNode* nextarc; //指向下一条弧的指针
}ArcNode; //弧结点
typedef struct {
ArcNode* AdjList[MAX_VERTEX_NUM]; //指向第一条依附该顶点的弧的指针
int vexnum, arcnum; //图的当前顶点和弧数
}Graph;
typedef struct //队列结构
{
int elem[MAXQSIZE]; //数据域
int front; // 队头指针
int rear; // 队尾指针
}SqQueue;
BOOL visited[MAX_VERTEX_NUM]; //全局变量——访问标志数组
void CreateGraph(Graph&); //生成图的邻接表
void DFSTraverse(Graph); //深度优先搜索遍历图
void DFS(Graph, int);
void Initial(SqQueue&); //初始化一个队列
bool QueueEmpty(SqQueue); //判断队列是否为空
bool EnQueue(SqQueue&, int); //将一个元素入队列
bool DeQueue(SqQueue&, int&); //将一个元素出队列
int firstAdjVex(Graph, int); //求图中某一顶点的第一个邻接顶点
int NextAdjVex(Graph, int, int); //求某一顶点的下一个邻接顶点
//构造邻接表结构的图G
void CreateGraph(Graph& G) {
int i;
int start, end;
ArcNode* s;
for (i = 1; i <= G.vexnum; i++)
G.AdjList[i] = NULL; //初始化指针数组
if (G.arcnum == 0) {
cout << "The graph have no edges." << endl;
}
else {
for (i = 1; i <= G.arcnum; i++)
{
cin >> start >> end; //输入弧的起点和终点
s = (ArcNode*)malloc(sizeof(ArcNode)); //生成一个弧结点
s->nextarc = G.AdjList[start]; //插入到邻接表中
s->adjvex = end;
G.AdjList[start] = s;
{
s = (ArcNode*)malloc(sizeof(ArcNode));
s->nextarc = G.AdjList[end];
s->adjvex = start;
G.AdjList[end] = s;
}
}
}
}
//深度优先遍历图G
void DFSTraverse(Graph G)
{
int i;
cout << "深度优先遍历结果为:" << endl;
for (i = 1; i <= G.vexnum; i++)
visited[i] = False; //访问标志数组初始化
for (i = 1; i <= G.vexnum; i++)
if (!visited[i]) DFS(G, i);
cout << endl;
}
//从第i个顶点出发递归地深度遍历图G
void DFS(Graph G, int i) {
int w;
visited[i] = True; //访问第i个顶点
cout << i << " ";
for (w = firstAdjVex(G, i); w > 0; w = NextAdjVex(G, i, w))
if (!visited[w]) DFS(G, w); //对尚未访问的邻接顶点w调用DFS
}
//在图G中寻找第v个顶点的第一个邻接顶点
int firstAdjVex(Graph G, int v)
{
if (!G.AdjList[v])
return 0;
else
return(G.AdjList[v]->adjvex);
}
//在图中寻找第v个顶点的相对于u的下一个邻接顶点
int NextAdjVex(Graph G, int v, int u)
{
ArcNode* p;
p = G.AdjList[v];
while (p->adjvex != u) p = p->nextarc; //在顶点v的弧链中找到顶点u
if (p->nextarc == NULL)
return 0; //若已经是最后一个结点,返回0
else
return(p->nextarc->adjvex); //返回下一个邻接顶点的序号
}
//队列初始化
void Initial(SqQueue& Q)
{
Q.front = Q.rear = 0;
}
//判断队列是否已空,若空返回True,否则返回False
bool QueueEmpty(SqQueue Q) {
if (Q.front == Q.rear)
return True;
else
return False;
}
//入队列,成功返回True,失败返回False
bool EnQueue(SqQueue& Q, int ch)
{
if ((Q.rear + 1) % MAXQSIZE == Q.front)
return False;
Q.elem[Q.rear] = ch;
Q.rear = (Q.rear + 1) % MAXQSIZE;
return True;
}
//出队列,成功返回True,并用ch返回该元素值,失败返回False
bool DeQueue(SqQueue& Q, int& ch)
{
if (Q.front == Q.rear)
return False;
ch = Q.elem[Q.front];
Q.front = (Q.front + 1) % MAXQSIZE;
return True;
}
int main() {
Graph G;
cout << "请输入无向图的顶点数和边数 :" << endl;
cin >> G.vexnum >> G.arcnum;
cout << "请输入弧的起点和终点:" << endl;
CreateGraph(G);
DFSTraverse(G);
return 0;
}