在计算图中任意两点之间的最短距离时,最小生成树(Minimum Spanning Tree, MST)算法并不直接适用,因为它主要用于找到连接所有顶点且权重之和最小的子图。然而,如果我们需要计算任意两点之间的最短距离,更适合使用单源最短路径算法,如Dijkstra算法或者Floyd-Warshall算法。
(1)Dijkstra算法适用于从单个源点到其他所有点的最短路径计算,它基于贪心策略,通过逐步扩展当前已知最短路径的集合来实现。
(2)Floyd-Warshall算法则适用于计算图中所有点对之间的最短路径,它通过动态规划的方式逐步优化路径长度矩阵来实现。
一、代码介绍
// 读取图的邻接矩阵和顶点坐标信息
bool readGraphFromFile(const char* filename, GraphMatrix& GM) {
ifstream infile(filename);
if (!infile) {
cerr << "Error: Unable to open the file " << filename << endl;
return false;
}
// 读取顶点数和边数
infile >> GM.VertexNum;
GM.EdgeNum = 0; // 初始化边数为0
// 读取顶点信息和坐标
for (int i = 0; i < GM.VertexNum; ++i) {
infile >> GM.Vertex[i] >> GM.X[i] >> GM.Y[i];
}
// 读取边的权值
for (int i = 0; i < GM.VertexNum; ++i) {
for (int j = 0; j < GM.VertexNum; ++j) {
infile >> GM.EdgeWeight[i][j];
if (GM.EdgeWeight[i][j] != -1) // 如果是有效边
GM.EdgeNum++; // 增加边数计数
}
}
infile.close();
return true;
}
// 保存图的邻接矩阵和顶点坐标信息到文件
bool saveGraphToFile(const char* filename, const GraphMatrix& GM) {
ofstream outfile(filename);
if (!outfile) {
cerr << "Error: Unable to create the file " << filename << endl;
return false;
}
// 写入顶点数和边数
outfile << GM.VertexNum << endl;
// 写入顶点信息和坐标
for (int i = 0; i < GM.VertexNum; ++i) {
outfile << GM.Vertex[i] << " " << GM.X[i] << " " << GM.Y[i] << endl;
}
// 写入边的权值
for (int i = 0; i < GM.VertexNum; ++i) {
for (int j = 0; j < GM.VertexNum; ++j) {
outfile << GM.EdgeWeight[i][j] << " ";
}
outfile << endl;
}
outfile.close();
return true;
}
1、readGraphFromFile 函数
功能:从指定文件中读取图的结构信息。
filename:待读取的文件名。
GM:图信息将存储在 GraphMatrix 结构体中。
返回值:函数执行成功返回 true,否则返回 false。
流程:
打开文件并检查是否成功打开,如果打开失败则输出错误信息并返回 false。
从文件中读取顶点数 GM.VertexNum。
初始化边数 GM.EdgeNum 为 0。
循环读取每个顶点的编号 GM.Vertex[i] 和坐标 (GM.X[i], GM.Y[i])。
读取邻接矩阵中每条边的权值 GM.EdgeWeight[i][j],并检查是否为 -1(通常表示无边),如果不是 -1 则增加 GM.EdgeNum,以统计图中实际存在的边数。
关闭文件并返回 true 表示读取成功。
2、saveGraphToFile 函数
功能:将图的结构信息和邻接矩阵以及顶点坐标信息保存到指定文件中。
参数:
filename:要保存的文件名。
GM:包含了图结构和信息的 GraphMatrix 结构体。
返回值:函数执行成功返回 true,否则返回 false。
流程:
打开文件并检查是否成功打开,如果打开失败则输出错误信息并返回 false。
将顶点数 GM.VertexNum 写入文件的第一行。
依次写入每个顶点的编号 GM.Vertex[i] 和坐标 (GM.X[i], GM.Y[i]) 到文件中。
以邻接矩阵的形式将边的权值 GM.EdgeWeight[i][j] 写入文件,每行对应一个顶点的连接情况。
关闭文件并返回 true 表示保存成功。
这两个函数共同实现了从文件中读取图的信息,并将图的信息保存到文件中的功能。
3、这段代码实现了Prim算法来生成一个无向图的最小生成树(Minimum Spanning Tree, MST)。
primMST 函数解释
变量定义和初始化:
parent[MaxNum]:用于存储最小生成树的边,即每个顶点的父节点。
key[MaxNum]:存储每个顶点的关键值,即该顶点与最小生成树的距离。
inMST[MaxNum]:标记顶点是否已经包含在最小生成树中。
构建最小生成树:
外层循环执行 GM.VertexNum - 1 次,每次选择一个顶点加入最小生成树。
内层循环用于选择关键值最小的顶点 u,将其标记为已经包含在最小生成树中 (inMST[u] = true)。
然后更新与顶点 u 相邻的顶点的关键值 key[v],如果发现更小的权值,则更新该顶点的父节点为 u。
double distance(int x1, int y1, int x2, int y2) {
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}
// Prim算法实现生成最小生成树
void primMST(const GraphMatrix& GM) {
int parent[MaxNum]; // 用于存储最小生成树的边
int key[MaxNum]; // 用于存储每个顶点的关键值,即与最小生成树的距离
// 标记顶点是否被包含在最小生成树中
bool inMST[MaxNum];
for (int i = 0; i < GM.VertexNum; ++i) {
key[i] = INT_MAX; // 初始化所有顶点的关键值为无穷大
inMST[i] = false; // 初始化所有顶点都不在最小生成树中
}
// 初始条件:第一个顶点作为最小生成树的根节点
key[0] = 0; // 选择第一个顶点的关键值为0,即第一个顶点加入最小生成树中
parent[0] = -1; // 第一个顶点没有父节点
// 将所有顶点包括到最小生成树中
for (int count = 0; count < GM.VertexNum - 1; ++count) {
// 选择关键值最小的顶点
int u = -1;
for (int v = 0; v < GM.VertexNum; ++v) {
if (!inMST[v] && (u == -1 || key[v] < key[u])) {
u = v;
}
}
// 将选中的顶点u包括到最小生成树中
inMST[u] = true;
// 更新与顶点u相邻的顶点的关键值
for (int v = 0; v < GM.VertexNum; ++v) {
if (GM.EdgeWeight[u][v] != -1 && !inMST[v] && GM.EdgeWeight[u][v] < key[v]) {
parent[v] = u;
key[v] = GM.EdgeWeight[u][v];
}
}
}
// 输出最小生成树的边
cout << "最小生成树的边:" << endl;
for (int i = 1; i < GM.VertexNum; ++i) {
cout << GM.Vertex[parent[i]] << " - " << GM.Vertex[i] << " 权值:" << GM.EdgeWeight[i][parent[i]] << endl;
}
cout << endl;
}
4、这段 main 函数主要完成了以下几个任务,通过操作图(GraphMatrix GM)来展示图的基本信息、计算两点之间的距离、生成最小生成树(MST)并保存结果,以及将图的数据保存到文件中。
(1)readGraphFromFile(filename, GM) 函数用于从文件中读取邻接矩阵数据和顶点坐标,并将其存储在 GM 结构体中。如果读取失败,程序返回 1。
(2)distance 函数计算了顶点 point1 和 point2 之间的欧几里得距离,并将结果输出到控制台。这里假设 distance 函数是已经定义好的,用于计算两点之间的距离。
(3)saveGraphToFile(saveFilename, GM) 函数用于将图的邻接矩阵数据和顶点坐标保存到名为 saved_graph.txt 的文件中。如果保存失败,程序返回 1。
int main() {
GraphMatrix GM;
// 从文件中读取邻接矩阵数据和顶点坐标
const char* filename = "graph.txt";
if (!readGraphFromFile(filename, GM)) {
// 如果读取失败,可以在这里处理错误或者进行其他操作
return 1;
}
// 输出顶点信息和坐标
cout << "顶点信息和坐标:" << endl;
for (int i = 0; i < GM.VertexNum; ++i) {
cout << GM.Vertex[i] << " (" << GM.X[i] << ", " << GM.Y[i] << ")" << endl;
}
cout << endl;
// 计算任意两点之间的距离
int point1 = 0, point2 = 1; // 示例:计算顶点0和1之间的距离
double dist = distance(GM.X[point1], GM.Y[point1], GM.X[point2], GM.Y[point2]);
cout << "顶点" << GM.Vertex[point1] << "和顶点" << GM.Vertex[point2] << "之间的距离为: " << dist << endl;
// 将计算结果保存到文件
ofstream resultFile("distance_results.txt");
if (resultFile) {
resultFile << "顶点" << GM.Vertex[point1] << "和顶点" << GM.Vertex[point2] << "之间的距离为: " << dist << endl;
resultFile.close();
cout << "计算结果已保存到 distance_results.txt" << endl;
} else {
cerr << "Error: Unable to create distance_results.txt" << endl;
return 1;
}
// 计算最小生成树并保存结果到文件
primMST(GM);
// 保存图的数据到文件
const char* saveFilename = "saved_graph.txt";
if (!saveGraphToFile(saveFilename, GM)) {
// 如果保存失败,可以在这里处理错误或者进行其他操作
return 1;
}
cout << "图数据已保存到文件 " << saveFilename << endl;
return 0;
}
二、完整代码
#include <iostream>
#include <fstream>
#include <cmath>
#include <limits>
using namespace std;
const int MaxNum = 10; // 最大顶点数
struct GraphMatrix {
int VertexNum; // 顶点数
int EdgeWeight[MaxNum][MaxNum]; // 边的权值
char Vertex[MaxNum]; // 顶点信息,例如 'A' 到 'Z'
int X[MaxNum]; // 顶点的X坐标
int Y[MaxNum]; // 顶点的Y坐标
int EdgeNum; // 边的数量
};
// 读取图的邻接矩阵和顶点坐标信息
bool readGraphFromFile(const char* filename, GraphMatrix& GM) {
ifstream infile(filename);
if (!infile) {
cerr << "Error: Unable to open the file " << filename << endl;
return false;
}
// 读取顶点数和边数
infile >> GM.VertexNum;
GM.EdgeNum = 0; // 初始化边数为0
// 读取顶点信息和坐标
for (int i = 0; i < GM.VertexNum; ++i) {
infile >> GM.Vertex[i] >> GM.X[i] >> GM.Y[i];
}
// 读取边的权值
for (int i = 0; i < GM.VertexNum; ++i) {
for (int j = 0; j < GM.VertexNum; ++j) {
infile >> GM.EdgeWeight[i][j];
if (GM.EdgeWeight[i][j] != -1) // 如果是有效边
GM.EdgeNum++; // 增加边数计数
}
}
infile.close();
return true;
}
// 保存图的邻接矩阵和顶点坐标信息到文件
bool saveGraphToFile(const char* filename, const GraphMatrix& GM) {
ofstream outfile(filename);
if (!outfile) {
cerr << "Error: Unable to create the file " << filename << endl;
return false;
}
// 写入顶点数和边数
outfile << GM.VertexNum << endl;
// 写入顶点信息和坐标
for (int i = 0; i < GM.VertexNum; ++i) {
outfile << GM.Vertex[i] << " " << GM.X[i] << " " << GM.Y[i] << endl;
}
// 写入边的权值
for (int i = 0; i < GM.VertexNum; ++i) {
for (int j = 0; j < GM.VertexNum; ++j) {
outfile << GM.EdgeWeight[i][j] << " ";
}
outfile << endl;
}
outfile.close();
return true;
}
// 计算两点之间的距离
double distance(int x1, int y1, int x2, int y2) {
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}
// Prim算法实现生成最小生成树
void primMST(const GraphMatrix& GM) {
int parent[MaxNum]; // 用于存储最小生成树的边
int key[MaxNum]; // 用于存储每个顶点的关键值,即与最小生成树的距离
// 标记顶点是否被包含在最小生成树中
bool inMST[MaxNum];
for (int i = 0; i < GM.VertexNum; ++i) {
key[i] = INT_MAX; // 初始化所有顶点的关键值为无穷大
inMST[i] = false; // 初始化所有顶点都不在最小生成树中
}
// 初始条件:第一个顶点作为最小生成树的根节点
key[0] = 0; // 选择第一个顶点的关键值为0,即第一个顶点加入最小生成树中
parent[0] = -1; // 第一个顶点没有父节点
// 将所有顶点包括到最小生成树中
for (int count = 0; count < GM.VertexNum - 1; ++count) {
// 选择关键值最小的顶点
int u = -1;
for (int v = 0; v < GM.VertexNum; ++v) {
if (!inMST[v] && (u == -1 || key[v] < key[u])) {
u = v;
}
}
// 将选中的顶点u包括到最小生成树中
inMST[u] = true;
// 更新与顶点u相邻的顶点的关键值
for (int v = 0; v < GM.VertexNum; ++v) {
if (GM.EdgeWeight[u][v] != -1 && !inMST[v] && GM.EdgeWeight[u][v] < key[v]) {
parent[v] = u;
key[v] = GM.EdgeWeight[u][v];
}
}
}
// 输出最小生成树的边
cout << "最小生成树的边:" << endl;
for (int i = 1; i < GM.VertexNum; ++i) {
cout << GM.Vertex[parent[i]] << " - " << GM.Vertex[i] << " 权值:" << GM.EdgeWeight[i][parent[i]] << endl;
}
cout << endl;
}
int main() {
GraphMatrix GM;
// 从文件中读取邻接矩阵数据和顶点坐标
const char* filename = "graph.txt";
if (!readGraphFromFile(filename, GM)) {
// 如果读取失败,可以在这里处理错误或者进行其他操作
return 1;
}
// 输出顶点信息和坐标
cout << "顶点信息和坐标:" << endl;
for (int i = 0; i < GM.VertexNum; ++i) {
cout << GM.Vertex[i] << " (" << GM.X[i] << ", " << GM.Y[i] << ")" << endl;
}
cout << endl;
// 计算任意两点之间的距离
int point1 = 0, point2 = 1; // 示例:计算顶点0和1之间的距离
double dist = distance(GM.X[point1], GM.Y[point1], GM.X[point2], GM.Y[point2]);
cout << "顶点" << GM.Vertex[point1] << "和顶点" << GM.Vertex[point2] << "之间的距离为: " << dist << endl;
// 将计算结果保存到文件
ofstream resultFile("distance_results.txt");
if (resultFile) {
resultFile << "顶点" << GM.Vertex[point1] << "和顶点" << GM.Vertex[point2] << "之间的距离为: " << dist << endl;
resultFile.close();
cout << "计算结果已保存到 distance_results.txt" << endl;
} else {
cerr << "Error: Unable to create distance_results.txt" << endl;
return 1;
}
// 计算最小生成树并保存结果到文件
primMST(GM);
// 保存图的数据到文件
const char* saveFilename = "saved_graph.txt";
if (!saveGraphToFile(saveFilename, GM)) {
// 如果保存失败,可以在这里处理错误或者进行其他操作
return 1;
}
cout << "图数据已保存到文件 " << saveFilename << endl;
return 0;
}
三、总结
上面的代码展示了如何基于邻接矩阵表示的图数据结构进行基本操作,包括数据的读取、处理和保存。它涵盖了从文件读取图数据、计算图中顶点之间的距离、生成最小生成树,并将结果保存到文件的完整流程。这种实现方式适合于小规模的图处理和算法实现,能够有效地展示图数据的结构和基本操作。