一、实验目的:
1、图的邻接表和邻接矩阵存储
2、图的各种遍历算法实现
3、最小生成树的算法实现
4、最短路径的算法实现
二、使用仪器、器材
微机一台
操作系统:WinXP
编程软件:C++
三、实验内容及原理
利用图的邻接表或邻接矩阵存储结构设计并实现各种操作算法(任选一种存储结构来实现算法)。
1、图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之;
2、图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法;
3、最小生成树的算法实现
利用普里姆(Prim)或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
4、最短路径的算法实现
利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
四、实验过程原始数据记录
该程序使用全程使用邻接表的方式存储
/*
1、图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之;
用 class 创建边结点arcnode ,顶点vnode ,邻接表 algraph
在邻接表 algraph 包含创建无向图函数 CreateUDG(),输出邻接表函数 showUDG()
2、图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法;
在邻接表 algraph 包含void DFS_AM( int v)深度优先搜索遍历函数,void BFS(int v)广度优先搜索遍历函数
此时定义全局数组 visited 用来判断该顶点是否已遍历
3、最小生成树的算法实现
利用普里姆(Prim)或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
在邻接表 algraph 包含void MiniSpanTree_Prim(int u) 用普里姆(Prim)算法创建最小树,
void showMini();按邻接表方式输出最小树
4、最短路径的算法实现
利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
在邻接表 algraph 包含void shortestpath_DIJ(int v0); //利用狄克斯特拉(Dijkstra)算法最短路径
*/
#include "pch.h"
#include <iostream>
#include < queue >
using namespace std;
//**************************************************边结点
class ArcNode
{
public:
int adjvex; //该边所指向结点的位置
int cost; //权值
ArcNode *nextarc; //指向下一条边的指针
};
//**************************************普里姆 最小生成树
class closedge
{
public:
int adjvex; //最小边所指向结点
int lowcost; //最小边权值
closedge *nextarc; //指向下一条边的指针
};
//*************************************************顶点信息
class VNode
{
public:
int data; //顶点的值
ArcNode *firstarc; //指向第一条依附该节点的边的指针
closedge *firstdge; //指向第一条依附该节点的边的指针(最小树)
};
//************************************************邻接表
class ALGraph
{
VNode *vertices = new VNode[50] ; //顶点
int vexnum, arcnum; //顶点数和边数
public:
void CreateUDG(); //用邻接表创建无向图
void showUDG(); //输出邻接表
void DFS_AM( int v); //从第v个顶点粗发进行深度优先搜索遍历
void BFS(int v); //从第v个顶点粗发进行广度优先搜索遍历
void MiniSpanTree_Prim(int u); //用普里姆(Prim)算法创建最小树
void showMini(); //按邻接表方式输出最小树
void shortestpath_DIJ(int v0); //利用狄克斯特拉(Dijkstra)算法最短路径
};
int main()
{
ALGraph G;
G.CreateUDG();
G.showUDG();
cout << endl << "深度优先遍历如下:";
G.DFS_AM(0);
cout << endl << "广度优先遍历如下:";
G.BFS(0);
G.MiniSpanTree_Prim(0);
G.showMini();
G.shortestpath_DIJ(0);
}
void ALGraph::CreateUDG() //无向图
{
/*******************自定义无向图,删掉下面建立题目图的代码,打开即可解锁
cout << "请依次输入顶点数和边数: ";
cin >> this->vexnum >> this->arcnum;
cout << "请对应序号输入结点值:" << endl;
for (int i = 0; i<this->vexnum; ++i)
{
cout << "序号为" << i << "的结点值: ";
cin >> this->vertices[i].data;
this->vertices[i].firstarc = NULL;
}
*/
//*****************建立题目中的无向图
this->vexnum = 7; this->arcnum = 9;
for (int i = 0; i < this->vexnum; ++i)
{
this->vertices[i].data = i;
cout << "序号为" << i << "的点的值为:" << this->vertices[i].data << endl;
this->vertices[i].firstarc = NULL;
}
cout << "请输入各条边依附的两个点的序号、该边的权值:" << endl;
for (int i = 0; i < this->arcnum; ++i)
{
cout << "第" << i+1 << "条边连接的两个节点的序号: ";
int v1, v2, c;
cin >> v1 >> v2 >> c;
ArcNode *p1 = new ArcNode; //生成新的边结点
p1->cost = c;
p1->adjvex = v2; //p1所指向v2的位置
p1->nextarc = this->vertices[v1].firstarc; //p1下一条边的指针指向v1的第一条边
this->vertices[v1].firstarc = p1;
ArcNode *p2 = new ArcNode; //生成新的边结点
p2->cost = c;
p2->adjvex = v1;
p2->nextarc = this->vertices[v2].firstarc;
this->vertices[v2].firstarc = p2;
}
}
void ALGraph::showUDG()
{
cout << "序号 结点值 ---> 相连点的序号 --->...." << endl;
for (int i = 0; i < this->vexnum; ++i)
{
if (vertices[i].firstarc != NULL) //若该节点没有相连的节点
{
cout << i << " " << this->vertices[i].data << " ";
ArcNode *p1 = new ArcNode;
p1 = vertices[i].firstarc;
while (p1!= NULL)
{
cout << "--->" << p1->adjvex;
p1 = p1->nextarc;
}
cout << endl;
}
else
{
cout << i << " " << this->vertices[i].data << " --->NULL" << endl;
}
}
}
bool *visited=new bool[50]; //全局数组,初值为true
void ALGraph::DFS_AM(int v)
{
cout << v;
visited[v] = false;
ArcNode *p = new ArcNode;
p = this->vertices[v].firstarc;
while (p != NULL)
{
int w = p->adjvex;
if (visited[w]) DFS_AM(w);
p = p->nextarc;
}
}
//利用深度优先时的数组visited,在深度优先后初值为false
void ALGraph::BFS(int v)
{
cout << v;
visited[v] = true;
queue< int > q;
q.push(v); //v进队
while (!q.empty())
{
int u = q.front();
q.pop();
ArcNode *p = new ArcNode;
p = this->vertices[u].firstarc;
while (p != NULL)
{
int w = p->adjvex;
if (!visited[w])
{
cout << w;
visited[w] = true;
q.push(w);
}
p = p->nextarc;
}
}
}
//利用以上数组visited,两次遍历后初值为true
void ALGraph::MiniSpanTree_Prim(int u)
{
for (int i = 0; i < this->vexnum; ++i) //初始化树的结点
{
this->vertices[i].firstdge = NULL;
}
visited[u] = false;
for (int i = 0; i < this->vexnum - 1; i++) //生成的边数为 顶点数-1
{
int v1, v2;
closedge *p1 = new closedge; //最小树的边
closedge *p2 = new closedge; //最小树的边
ArcNode *p = new ArcNode;
p1->lowcost = 1000;
for (int j = 0; j < this->vexnum; j++)
{
if (visited[j] == false)
{
p = this->vertices[j].firstarc;
if (p->cost < p1->lowcost&&visited[p->adjvex] == true)
{
v2 = p->adjvex;
v1 = j;
p1->adjvex = v2;
p1->lowcost = p->cost;
}
while (p != NULL) //遍历目前结点的边
{
p = p->nextarc;
if (p != NULL)
{
if (p->cost < p1->lowcost&&visited[p->adjvex] == true) //该边的权值小于最小边且该边所指结点未划入最小树
{
v2 = p->adjvex;
v1 = j;
p1->adjvex = v2;
p1->lowcost = p->cost;
}
}
}
}
}
visited[p1->adjvex] = false; //标记该节点以划入树中
p1->nextarc = this->vertices[v1].firstdge; //p1下一条边的指针指向v1的第一条边
this->vertices[v1].firstdge = p1;
p2->lowcost = p1->lowcost;
p2->adjvex = v1;
p2->nextarc = this->vertices[v2].firstdge;
this->vertices[v2].firstdge = p2;
}
}
void ALGraph::showMini()
{
cout << endl << "最小树的邻接表:" << endl;
cout << "序号 结点值 --权值--> 相连点的序号 --->...." << endl;
for (int i = 0; i < this->vexnum; ++i)
{
cout << i << " " << this->vertices[i].data << " ";
closedge *p1 = new closedge;
p1 = vertices[i].firstdge;
while (p1 != NULL)
{
cout << " --"<<p1->lowcost<<"--> " << p1->adjvex ;
p1 = p1->nextarc;
}
cout << endl;
}
}
void ALGraph::shortestpath_DIJ(int v0)
{
int *path = new int[50]; //前驱数组
int *D = new int[50]; //最短路径长度数组
bool *S = new bool[50]; //记录是否求得v0到其他顶点最短路径 数组,初值为true
ArcNode *p1 = new ArcNode;
p1 = vertices[v0].firstarc;
for (int i = 0; i < this->vexnum; ++i)
{
S[i] = true;
path[i] = -1; //初始化前驱全为-1,即v0与v之间无弧
D[i] = 1000; //无弧则路径无限大
}
while (p1 != NULL)
{
path[p1->adjvex]=v0; //v0与v之间有弧,前驱为v0
D[p1->adjvex] = p1->cost; //有弧则路径为权值
p1 = p1->nextarc;
}
S[v0] = false;
path[v0] = v0;
D[v0] = 0;
for (int i = 1; i < this->vexnum; ++i) //对其他vexnum-1个顶点求最短路径
{
int min = 1000;
int v = 0;
for (int w = 0; w < this->vexnum; ++w)
if (S[w] == true && D[w] < min) //寻找当前的最短路径
{
min = D[w];
v = w;
}
S[v] = false;
ArcNode *p2 = new ArcNode;
int *Dvw = new int[50]; //计算v与其他顶点的最短路径
p2 = vertices[v].firstarc;
for (int i = 0; i < this->vexnum; ++i)
{
Dvw[i] = 1000; //无弧则路径无限大}
}
while (p2 != NULL)
{
Dvw[p2->adjvex] = p2->cost; //有弧则路径为权值
p2 = p2->nextarc;
}
for (int w = 0; w < this->vexnum; ++w)
if (S[w] == true && (D[v] + Dvw[w]) < D[w]) //若转弯比直走的权值小,则修改路径长度
{
D[w] = D[v] + Dvw[w];
path[w] = v;
}
}
cout << endl << "显示结点0到其他结点的最短路径:" << endl;
for (int i = 0; i < this->vexnum; ++i)
{
if (path[i] != -1)
{
cout << vertices[i].data<<"--->";
int p = path[i];
while (p != v0)
{
cout << vertices[p].data<<"--->";
p = path[p];
}
cout << vertices[v0].data << endl;
}
else cout << vertices[v0].data << "与" << vertices[i].data << "不连通" << endl;
}
}
五、实验结果及分析
结点的值已在代码中按题目要求赋予,只需输入每条边连接的两个结点的序号,便可依次输出该图的邻接表、深度优先遍历结果、广度优先遍历结果、最小生成树的邻接表、结点0到其他结点的最短路径。该程序使用于连通无向图。