一、实验目的:
1、图的邻接表和邻接矩阵存储
2、图的各种遍历算法实现
3、最小生成树的算法实现
4、最短路径的算法实现
二、使用仪器、器材
微机一台
操作系统:Win7及以上
编程软件:C/C++
三、实验内容及原理
利用图的邻接表或邻接矩阵存储结构设计并实现各种操作算法(任选一种存储结构来实现算法)。
1.图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之;
2.图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法;
3、最小生成树的算法实现
利用普里姆(Prim)算法或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
4、最短路径的算法实现
利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
四、实验过程原始数据记录
截屏及解读
1.建立下图的邻接表或邻接矩阵,并输出之;
邻接表
#include<iostream>
#include<queue>
using namespace std;
#define Maxint 32767 //极大值
constexpr auto MVNum = 100; //最大顶点数
struct AMGraph
{
char vexs[MVNum]; //顶点表
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum;
};
int LocateVex(AMGraph G, char v)
{
for (int i = 0; i < G.vexnum; i++)
{
if (G.vexs[i] == v)
return i;
}
cout << "顶点输入错误!" << endl;
return -1;
}
void createUDN(AMGraph& G)
{
cout << "请输入总顶点数:";
cin >> G.vexnum;
cout << "请输入总边数:";
cin >> G.arcnum;
cout << "请输入点的信息:" << endl;
for (int i = 0; i < G.vexnum; i++)
cin >> G.vexs[i];
for (int i = 0; i < G.vexnum; i++)
for (int j = 0; j < G.vexnum; j++)
G.arcs[i][j] = Maxint; //初始化邻接矩阵,边的权值均置为极大值
for (int k = 0; k < G.arcnum; k++)
{
char v1, v2; //两顶点
int w; //权值
cout << "请输入第" << k + 1 << "条边的两个顶点及权值:";
cin >> v1 >> v2 >> w;
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
if (i != -1 && j != -1) //无向图
{
G.arcs[i][j] = w;
G.arcs[j][i] = w;
}
}
}
int main()
{
AMGraph G;
createUDN(G);
return 0;
}
2.以0结点为起点实现上述图的深度优先和广度优先遍历算法;
以下代码是在上面代码的基础上实现的
bool visited[MVNum];
//采用邻接矩阵表示图的深度优先搜索遍历
void DFS_AM(AMGraph G, int v)
{
cout << 'v' << G.vexs[v];
visited[v] = true;
for (int w = 0; w < G.vexnum; w++)
{
if ((G.arcs[v][w] < Maxint) && (!visited[w]))
DFS_AM(G, w);
}
}
void TraDFS(AMGraph G)
{
for (int i = 0; i < G.vexnum; i++)
visited[i] = false;
for (int i = 0; i < G.vexnum; i++)
{
if (!visited[i])
{
DFS_AM(G, i);
cout << ' '; //看有多少个连通子图
}
}
}
//广度优先访问连通图
void BFS(AMGraph G, int v)
{
cout << 'v' << G.vexs[v];
visited[v] = true;
queue<int> Q;
Q.push(v);
int u;
while (!Q.empty())
{
u = Q.front();
Q.pop();
for (int w = 0; w < G.vexnum; w++)
{
if ((!visited[w]) && (G.arcs[u][w] < Maxint))
{
cout << "v" << G.vexs[w];
visited[w] = true;
Q.push(w);
}
}
}
}
//G可能是非连通图
void TraBFS(AMGraph G)
{
for (int i = 0; i < G.vexnum; i++)
visited[i] = false;
for (int i = 0; i < G.vexnum; i++)
{
if (!visited[i])
{
BFS(G, i);
cout << ' '; //区分访问了多少个连通子图
}
}
}
int main()
{
AMGraph G;
createUDN(G);
for (int i = 0; i < G.vexnum; i++)
cout << '\t' << 'v' << G.vexs[i];
cout << endl;
for (int i = 0; i < G.vexnum; i++)
{
cout << 'v' << i << '\t';
for (int j = 0; j < G.vexnum; j++)
cout << G.arcs[i][j] << '\t';
cout << endl;
}
cout << "\n请输入起点在顶点表中的序号(0为第一个):";
int v;
cin >> v;
cout << "\n深度优先遍历图:" << endl;
TraDFS(G);
cout << endl;
cout << "\n广度优先遍历图:" << endl;
TraBFS(G);
cout << endl;
return 0;
3.利用普里姆(Prim)算法或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
#include<iostream>
#include<algorithm>
#define Maxint 32767 //极大值
using namespace std;
constexpr auto MVNum = 100; //最大顶点数
struct AMGraph
{
char vexs[MVNum]; //顶点表
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum;
};
int LocateVex(AMGraph G, char v)
{
for (int i = 0; i < G.vexnum; i++)
{
if (G.vexs[i] == v)
return i;
}
cout << "顶点输入错误!" << endl;
return -1;
}
//普里姆算法
struct
{
int adjvex;
int lowcost;
}closedg[MVNum];
void MinSpanTree_Prim(AMGraph G, int u)
{
int length = 0; //带权路径长度
int k = u; //k为顶点u的下标
for (int j = 0; j < G.vexnum; j++) //对V-U的每一个顶点vj,初始化closedg[j]
{
if (j != k)
closedg[j] = { u,G.arcs[k][j] };
}
closedg[k].lowcost = 0;
for (int i = 1; i < G.vexnum; i++) //选择其余n-1个顶点,生成n-1条边
{
int mincost = Maxint; //记录与U相连的最小边的顶点下标和最小边权值
for (int j = 0; j < G.vexnum; j++)
{
if (closedg[j].lowcost > 0 && mincost > closedg[j].lowcost)
{
mincost = closedg[j].lowcost;
k = j;
}
}
//输出prim算法最小生成树形成路径
cout << 'v' << G.vexs[k] << ':' << mincost << endl;
length += mincost;
closedg[k].lowcost = 0;
for (int j = 0; j < G.vexnum; j++) //新顶点并入U后重新选择最小边
{
if (G.arcs[k][j] < closedg[j].lowcost)
closedg[j] = { G.vexs[k], G.arcs[k][j] };
}
}
cout << "该路径的权值为:" << length << endl;; //输出最小权值
}
//克鲁斯卡尔算法
struct Edge
{
char head;
char tail; //边的头和尾
int lowcost; //边上的权值
}edge[MVNum];
int Vexset[MVNum]; //辅助数组
void MiniSpanTree_Kruskal(AMGraph G)
{
//冒泡排序
for (int i = 0; i < G.arcnum; i++)
{
for (int j = 0; j < G.arcnum - i - 1; j++)
{
if (edge[j].lowcost > edge[j + 1].lowcost)
{
Edge temp = edge[j];
edge[j] = edge[j + 1];
edge[j + 1] = temp;
}
}
}
for (int i = 0; i < G.vexnum; i++)
Vexset[i] = i; //各顶点自成一个连通分量
for (int i = 0; i < G.arcnum; i++)
{
int v1 = LocateVex(G, edge[i].head);
int v2 = LocateVex(G, edge[i].tail); //起点和终点的下标
int vs1 = Vexset[v1];
int vs2 = Vexset[v2]; //获取边起点/终点所在的连通分量
if (vs1 != vs2)
{
cout << 'v' << edge[i].head << "--" << 'v' << edge[i].tail << " 权值:" << edge[i].lowcost << endl; //输出此边
for (int j = 0; j < G.vexnum; j++)
{
if (Vexset[j] == vs2)
Vexset[j] = vs1; //合并两个连通分量
}
}
}
}
void createUDN(AMGraph& G)
{
cout << "请输入总顶点数:";
cin >> G.vexnum;
cout << "请输入总边数:";
cin >> G.arcnum;
cout << "请输入点的信息(顶点表):" << endl;
for (int i = 0; i < G.vexnum; i++)
cin >> G.vexs[i];
for (int i = 0; i < G.vexnum; i++)
for (int j = 0; j < G.vexnum; j++)
G.arcs[i][j] = Maxint; //初始化邻接矩阵,边的权值均置为极大值
for (int k = 0; k < G.arcnum; k++)
{
char v1, v2; //两顶点
int w; //权值
cout << "请输入第" << k + 1 << "条边的两个顶点及权值:";
cin >> v1 >> v2 >> w;
edge[k] = { v1,v2,w };
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
if (i != -1 && j != -1) //无向图
{
G.arcs[i][j] = w;
G.arcs[j][i] = w;
}
}
}
int main()
{
AMGraph G;
createUDN(G);
int u;
cout << "\n请输入起点在顶点表中的序号(0为第一个):";
cin >> u;
cout << "prim算法最小路径为:\n" << 'v' << G.vexs[u] << endl;
MinSpanTree_Prim(G, u);
cout << "\nKruskal算法合并边的过程为:" << endl;
MiniSpanTree_Kruskal(G);
return 0;
}
4.利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
#include<iostream>
using namespace std;
#define Maxint 32767 //极大值
constexpr auto MVNum = 100; //最大顶点数
struct AMGraph
{
char vexs[MVNum]; //顶点表
int arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum;
};
int LocateVex(AMGraph G, char v)
{
for (int i = 0; i < G.vexnum; i++)
{
if (G.vexs[i] == v)
return i;
}
cout << "顶点输入错误!" << endl;
return -1;
}
void createUDN(AMGraph& G)
{
cout << "请输入总顶点数:";
cin >> G.vexnum;
cout << "请输入总边数:";
cin >> G.arcnum;
cout << "请输入点的信息:" << endl;
for (int i = 0; i < G.vexnum; i++)
cin >> G.vexs[i];
for (int i = 0; i < G.vexnum; i++)
for (int j = 0; j < G.vexnum; j++)
G.arcs[i][j] = Maxint; //初始化邻接矩阵,边的权值均置为极大值
for (int k = 0; k < G.arcnum; k++)
{
char v1, v2; //两顶点
int w; //权值
cout << "请输入第" << k + 1 << "条边的两个顶点及权值:";
cin >> v1 >> v2 >> w;
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
if (i != -1 && j != -1) //无向图
{
G.arcs[i][j] = w;
G.arcs[j][i] = w;
}
}
}
void ShorteastPath_DIJ(AMGraph G, int v0)
{
int n = G.vexnum; //点数
int m = G.arcnum; //边数
bool* S = new bool[n]; //记录是否已被确定最短路径长度
int* D = new int[m]; //记录当前最短路径的长度
int* Path = new int[n]; //记录最短路径上点的直接前驱
for (int v = 0; v < n; v++) //n个顶点依次初始化
{
S[v] = false; //S初始为空集
D[v] = G.arcs[v0][v]; //将v0到各个终点的最短路径长度初始化为弧上的权值
if (D[v] < Maxint) //如果v0和v之间有弧,则将v的前驱设为v0
Path[v] = v0;
else
Path[v] = -1; //如果v0和v之间无弧,则将v的前驱设为-1
}
S[v0] = true; //将v0加入S
D[v0] = 0; //源点到源点的距离为0
for (int i = 0; i < n; i++) //对其余n-1个顶点,依次进行计算
{
int v = v0;
int min = Maxint;
for (int w = 0; w < n; w++)
{
if (!S[w] && D[w] < min)
{
v = w;
min = D[w]; //选择一条当前的最短路径,终点为v
}
}
S[v] = true; //将v加入S
for (int w = 0; w < n; w++) //更新从v0出发到集合V-S上所有顶点的最短路径长度
{
if (!S[w] && (D[v] + G.arcs[v][w] < D[w]))
{
D[w] = D[v] + G.arcs[v][w]; //更新D[w]
Path[w] = v; //更改w的前驱为v
}
}
}
cout << "节点 " << "最短路径长度 " << " 前驱" << endl;
for (int i = 0; i < n; ++i)
cout << " v" << G.vexs[i] << " " << D[i] << " " << Path[i] << endl;
delete S;
delete D;
delete Path;
}
int main()
{
AMGraph G;
createUDN(G);
cout << "\n请输入起点在顶点表中的序号(0为第一个):";
int v;
cin >> v;
cout << "\n迪杰斯特拉算法求解过程为:" << endl;
ShorteastPath_DIJ(G, v);
return 0;
}