当前位置: 首页 > news >正文

【数据结构】基于Prim算法的最小生成树

【实验目的】

1.掌握图的邻接矩阵表示法。

2.掌握求解最小生成树的Prim算法。

【实验内容】

1、问题描述 假设 n 个城市之间构建通信网,那么怎样能够做到在最节省通信线路经费 的条件下建立这个通信网呢?可以用Prim算法构建连通这n个结点所需的n-1 条线路,从而最大可能的节省通信线路经费。

2、输入要求 多组数据,每组数据有m+3行。第一行为两个整数n和m,分别代表城市 个数n和路径条数m。第二行有n个字符,代表每个城市的编号。第三行到第 m+2行每行有两个字符a和b和一个整数d,代表从城市a到城市b的通信线 路经费。 第m+3行代表起始顶点编号。当n和m都等于0时,输入结束。

3、输出要求 第1行打印所构造的最小生成树通信线路的各条边,如0-5 5-4 第2行打印最小生成树通信线路的总经费 第3行开始打印最小生成树通信线路所对应的邻接矩阵。

输入样例:

在这里给出一组输入。例如:

7 9
0 1 2 3 4 5 6
0 1 28
0 5 10
1 2 16
1 6 14
2 3 12
3 4 22
3 6 18
4 5 25
4 6 24
0 0

输出样例:

在这里给出相应的输出。例如:

0-5 5-4 4-3 3-2 2-1 1-6 
99
∞ 28 ∞ ∞ ∞ 10 ∞ 
28 ∞ 16 ∞ ∞ ∞ 14 
∞ 16 ∞ 12 ∞ ∞ ∞ 
∞ ∞ 12 ∞ 22 ∞ 18 
∞ ∞ ∞ 22 ∞ 25 24 
10 ∞ ∞ ∞ 25 ∞ ∞ 
∞ 14 ∞ 18 24 ∞ ∞ 

实现步骤:

1.图的存储与初始化

  1. 图的表示:采用邻接矩阵(AMGraph结构)存储无向网,其中包含:

    • 顶点表(vexs):存储顶点的字符信息;
    • 邻接矩阵(arcs):arcs[i][j]表示顶点i与顶点j之间边的权值,若无边则为极大值MaxInt
    • 顶点数(vexnum)和边数(arcnum)。
  2. 图的构造:通过CreateUDN函数输入顶点数、边数、顶点信息及边的权值,初始化邻接矩阵(初始值为MaxInt),再根据输入的边信息填充邻接矩阵(无向网对称填充,arcs[i][j] = arcs[j][i])。

2.Prim 算法核心流程

Prim 算法的核心思想是:从一个初始顶点出发,逐步将 “已加入生成树的顶点集” 与 “未加入的顶点集” 之间权值最小的边所连接的顶点纳入生成树,最终形成包含所有顶点的最小生成树(无环且权值和最小)。

步骤 1:初始化生成树顶点集
  • 定义数组vex1记录已加入生成树的顶点下标(初始值均为MaxInt,表示未使用);
  • 选择初始顶点(代码中默认从下标为0的顶点开始),将其存入vex1[0],即vex1[0] = 0
步骤 2:循环查找最小边并扩展生成树

循环的终止条件:已加入生成树的顶点数等于图的总顶点数(即vex1中有效顶点数等于G.vexnum)。

每次循环执行以下操作:

  1. 寻找当前最小边

    • 遍历vex1中所有已加入生成树的顶点(记为u);
    • 对每个u,遍历图中所有顶点(记为v),筛选出 “v未加入生成树”(通过FindInVex函数判断v不在vex1中)且 “uv之间存在边”(arcs[u][v] < MaxInt)的边;
    • 在所有符合条件的边中,找到权值最小的边,记录其权值(minEdge)、起点(vexBegin,即u)和终点(vexEnd,即v)。
  2. 扩展生成树

    • 将找到的最小边的终点vexEnd加入vex1数组(作为新的已加入顶点);
    • 记录该边(输出vexBegin-vexEnd),并将其权值累加到总权值sum中。
步骤 3:输出结果
  • 循环结束后,输出最小生成树的所有边;
  • 输出最小生成树的总权值sum

3.辅助函数的作用

  • LocateVex:根据顶点字符查找其在顶点表中的下标,用于构造邻接矩阵时定位顶点;
  • Show:打印邻接矩阵,用于验证图的存储是否正确;
  • LengthVex:计算vex1中已加入生成树的顶点数(统计非MaxInt的元素个数);
  • FindInVex:判断顶点v是否已加入生成树(检查v是否在vex1中)。

C++完整代码:

#include<iostream>
using namespace std;
#define MaxInt 32767  //表示极大值 
#define MVNum 100  //最大顶点数 
typedef char VerTexType;   //设置顶点类型为字符型 
typedef int ArcType;    //设置权重为整型 
typedef struct
{VerTexType vexs[MVNum];    //顶点表 ArcType arcs[MVNum][MVNum];   //邻接矩阵 int vexnum, arcnum;  //顶点数和边数 
}AMGraph;
//从顶点表中查找顶点
int LocateVex(AMGraph G, VerTexType u);
//构造无向网
int CreateUDN(AMGraph& G);
void Show(AMGraph G);   //打印邻接矩阵
int LengthVex(int vex1[]);	// 返回当前vex数组的长度 
int FindInVex(int vex[], int e);	// 查看vex数组中是否有元素e,有则返回1,无则返回0 
void MiniSpanTree_Prim(AMGraph G);	// Prim算法最小生成树 
int main()
{while (1){AMGraph G;CreateUDN(G);if (G.vexnum == 0 && G.arcnum == 0){break;}MiniSpanTree_Prim(G);Show(G);}return 0;
}
//从顶点表中查找顶点
int LocateVex(AMGraph G, VerTexType u)
{int i;for (i = 0;i < G.vexnum;i++){if (u == G.vexs[i]){return i;}}return -1;
}
//构造无向网
int CreateUDN(AMGraph& G)
{int i, j, k;cin >> G.vexnum >> G.arcnum;     //输入顶点数和边数 for (i = 0;i < G.vexnum;i++){cin >> G.vexs[i];       //顶点表}for (i = 0;i < G.vexnum;i++){for (j = 0;j < G.vexnum;j++){G.arcs[i][j] = MaxInt;    //初始化邻接矩阵 }}for (k = 0;k < G.arcnum;k++){    //构造邻接矩阵 VerTexType v1, v2;ArcType w;cin >> v1 >> v2 >> w;i = LocateVex(G, v1);j = LocateVex(G, v2);G.arcs[i][j] = w;  //边(v1,v2)权重置为w G.arcs[j][i] = G.arcs[i][j];   //无向网,对称 }return 1;
}
void Show(AMGraph G)   //打印邻接矩阵
{int i, j;for (i = 0;i < G.vexnum;i++){for (j = 0;j < G.vexnum;j++){if (G.arcs[i][j] == MaxInt){cout << "∞" << ' ';}else{cout << G.arcs[i][j] << ' ';}}cout << endl;}}
int LengthVex(int vex1[])	// 返回当前vex数组的长度 
{int i, num = 0;for (i = 0; i < MVNum; i++){if (vex1[i] != MaxInt)num++;}return num;
}
int FindInVex(int vex[], int e)	// 查看vex数组中是否有元素e,有则返回1,无则返回0 
{int i;for (i = 0; i < MVNum; i++){if (vex[i] == e)return 1;}return 0;
}
void MiniSpanTree_Prim(AMGraph G)	// Prim算法最小生成树 
{int minEdge, sum = 0; 	// 记录权值最小的边 int vex1[MVNum];	// 记录已连入生成树的顶点下标,数组下标表示先后顺序 int i, j, k = 0;int vexBegin, vexEnd;	// 边起点和边终点 for (i = 0; i < MVNum; i++)	// 将数组中的值全部初始化为无限 {vex1[i] = MaxInt;}vex1[0] = 0;	// 从顶点v0开始生成树while (k < LengthVex(vex1)){minEdge = MaxInt;for (i = 0; i < LengthVex(vex1); i++){for (j = 0; j < G.vexnum; j++){if (G.arcs[vex1[i]][j] < minEdge && !FindInVex(vex1, j))// 找还未被并入最小生成树的权值最小边 {minEdge = G.arcs[vex1[i]][j];	// 记录权值 vexBegin = vex1[i];	// 记下边的起点和终点 vexEnd = j;}}}k++;if (minEdge != MaxInt){vex1[k] = vexEnd;	// 新顶点加入生成树 cout << vexBegin << "-" << vexEnd << " ";sum = sum + minEdge;}}cout << endl;cout << sum << endl;
}

Python完整代码:

import sysMaxInt = 32767  # 表示极大值
MVNum = 100      # 最大顶点数class AMGraph:def __init__(self):self.vexs = []  # 顶点表(存储顶点字符)self.arcs = []  # 邻接矩阵(二维列表)self.vexnum = 0  # 顶点数self.arcnum = 0  # 边数def LocateVex(G, u):"""从顶点表中查找顶点u的索引,找不到返回-1"""for i in range(G.vexnum):if G.vexs[i] == u:return ireturn -1def CreateUDN(G):"""构造无向网,返回1表示成功,输入0 0时终止"""# 读取顶点数和边数line = sys.stdin.readline()if not line:G.vexnum, G.arcnum = 0, 0return 0parts = line.strip().split()if len(parts) != 2:G.vexnum, G.arcnum = 0, 0return 0G.vexnum, G.arcnum = map(int, parts)# 若顶点数和边数都为0,退出if G.vexnum == 0 and G.arcnum == 0:return 0# 读取顶点表vexs_line = sys.stdin.readline().strip().split()G.vexs = vexs_line[:G.vexnum]  # 确保只取指定数量的顶点# 初始化邻接矩阵为极大值G.arcs = [[MaxInt for _ in range(G.vexnum)] for _ in range(G.vexnum)]# 读取边信息并填充邻接矩阵for _ in range(G.arcnum):edge_line = sys.stdin.readline().strip().split()if len(edge_line) != 3:continue  # 忽略格式错误的行v1, v2, w = edge_line[0], edge_line[1], int(edge_line[2])i = LocateVex(G, v1)j = LocateVex(G, v2)if i != -1 and j != -1:G.arcs[i][j] = wG.arcs[j][i] = w  # 无向网对称return 1def Show(G):"""打印邻接矩阵"""for i in range(G.vexnum):row = []for j in range(G.vexnum):if G.arcs[i][j] == MaxInt:row.append("∞")else:row.append(str(G.arcs[i][j]))print(" ".join(row))def LengthVex(vex1):"""返回当前vex1数组中已加入生成树的顶点数(非MaxInt的元素个数)"""return sum(1 for x in vex1 if x != MaxInt)def FindInVex(vex1, e):"""判断顶点e是否在vex1数组中,存在返回True,否则返回False"""return e in vex1def MiniSpanTree_Prim(G):"""Prim算法求解最小生成树"""if G.vexnum == 0:returnmin_edge = MaxInttotal = 0  # 总权值vex1 = [MaxInt] * MVNum  # 存储已加入生成树的顶点索引# 初始化:从索引为0的顶点开始vex1[0] = 0k = 0  # 循环计数器while k < LengthVex(vex1):min_edge = MaxIntvex_begin = -1vex_end = -1# 遍历已加入生成树的顶点current_len = LengthVex(vex1)for i in range(current_len):u = vex1[i]# 遍历所有顶点,找未加入且权值最小的边for v in range(G.vexnum):if G.arcs[u][v] < min_edge and not FindInVex(vex1, v):min_edge = G.arcs[u][v]vex_begin = uvex_end = vk += 1if min_edge != MaxInt:# 将新顶点加入生成树vex1[k] = vex_endprint(f"{vex_begin}-{vex_end} ", end="")total += min_edgeprint()  # 换行print(total)def main():while True:G = AMGraph()if not CreateUDN(G):break  # 输入0 0时退出MiniSpanTree_Prim(G)Show(G)if __name__ == "__main__":main()

Java完整代码:

import java.util.Scanner;public class PrimMST {private static final int MaxInt = 32767; // 表示极大值private static final int MVNum = 100;    // 最大顶点数// 图的邻接矩阵表示static class AMGraph {char[] vexs;       // 顶点表int[][] arcs;      // 邻接矩阵int vexnum;        // 顶点数int arcnum;        // 边数public AMGraph() {vexs = new char[MVNum];arcs = new int[MVNum][MVNum];vexnum = 0;arcnum = 0;}}// 从顶点表中查找顶点u的索引,找不到返回-1private static int locateVex(AMGraph g, char u) {for (int i = 0; i < g.vexnum; i++) {if (g.vexs[i] == u) {return i;}}return -1;}// 构造无向网,返回1表示成功,输入0 0时返回0终止private static int createUDN(AMGraph g, Scanner scanner) {// 读取顶点数和边数int vexnum = scanner.nextInt();int arcnum = scanner.nextInt();g.vexnum = vexnum;g.arcnum = arcnum;// 若顶点数和边数都为0,终止if (vexnum == 0 && arcnum == 0) {return 0;}// 读取顶点表(忽略可能的换行符,确保读取正确)scanner.nextLine(); // 消耗上一行的换行String vexsLine = scanner.nextLine().trim();String[] vexsArr = vexsLine.split(" ");for (int i = 0; i < vexnum; i++) {g.vexs[i] = vexsArr[i].charAt(0); // 取单个字符作为顶点}// 初始化邻接矩阵为极大值for (int i = 0; i < vexnum; i++) {for (int j = 0; j < vexnum; j++) {g.arcs[i][j] = MaxInt;}}// 读取边信息并填充邻接矩阵for (int k = 0; k < arcnum; k++) {char v1 = scanner.next().charAt(0);char v2 = scanner.next().charAt(0);int w = scanner.nextInt();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; // 无向网对称存储}}return 1;}// 打印邻接矩阵private static void show(AMGraph g) {for (int i = 0; i < g.vexnum; i++) {for (int j = 0; j < g.vexnum; j++) {if (g.arcs[i][j] == MaxInt) {System.out.print("∞ ");} else {System.out.print(g.arcs[i][j] + " ");}}System.out.println();}}// 返回当前vex数组中已加入生成树的顶点数(非MaxInt的元素个数)private static int lengthVex(int[] vex1) {int num = 0;for (int i = 0; i < MVNum; i++) {if (vex1[i] != MaxInt) {num++;}}return num;}// 查看vex数组中是否有元素e,有则返回true,无则返回falseprivate static boolean findInVex(int[] vex1, int e) {for (int i = 0; i < MVNum; i++) {if (vex1[i] == e) {return true;}}return false;}// Prim算法求解最小生成树private static void miniSpanTreePrim(AMGraph g) {if (g.vexnum == 0) {return;}int minEdge;int sum = 0; // 总权值int[] vex1 = new int[MVNum]; // 存储已加入生成树的顶点索引// 初始化数组为极大值for (int i = 0; i < MVNum; i++) {vex1[i] = MaxInt;}vex1[0] = 0; // 从索引为0的顶点开始生成树int k = 0; // 循环计数器while (k < lengthVex(vex1)) {minEdge = MaxInt;int vexBegin = -1;int vexEnd = -1;// 遍历已加入生成树的顶点,寻找最小边int currentLen = lengthVex(vex1);for (int i = 0; i < currentLen; i++) {int u = vex1[i];// 遍历所有顶点,找未加入且权值最小的边for (int v = 0; v < g.vexnum; v++) {if (g.arcs[u][v] < minEdge && !findInVex(vex1, v)) {minEdge = g.arcs[u][v];vexBegin = u;vexEnd = v;}}}k++;if (minEdge != MaxInt) {vex1[k] = vexEnd; // 新顶点加入生成树System.out.print(vexBegin + "-" + vexEnd + " ");sum += minEdge;}}System.out.println();System.out.println(sum);}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (true) {AMGraph g = new AMGraph();int flag = createUDN(g, scanner);if (flag == 0) {break; // 输入0 0时退出}miniSpanTreePrim(g);show(g);}scanner.close();}
}

总结:

本文介绍了使用Prim算法构建通信网最小生成树的实验过程。实验通过邻接矩阵存储城市间的通信线路费用,采用Prim算法逐步选择最小权值边,最终生成总费用最低的通信网络。实验内容包括图的初始化、Prim算法的实现步骤(初始化顶点集、查找最小边、扩展生成树)以及结果输出(最小生成树边、总费用和邻接矩阵)。代码实现涵盖C++、Python和Java三种语言版本,均支持多组数据输入,并验证了算法的正确性。该实验帮助学生掌握图的邻接矩阵表示和Prim算法的核心思想。

http://www.dtcms.com/a/519682.html

相关文章:

  • Snipaste (截图贴图工具) 精准截图 中文免费版
  • C语言内存机制深度解析:指针运算、数组与字符串实战指南
  • 强化学习 深度学习 深度强化学习 有什么区别
  • 《FastAPI零基础入门与进阶实战》第23篇:mysql/HeidiSQL安装与利用HeidiSQL数据迁移
  • 可克达拉市建设局网站番禺厂家关键词优化
  • 注册公司在哪个网站系统微信crm系统如何添加
  • 深入 YOLOv5 数据增强:从 create_dataloader 到马赛克范围限定
  • 如果战国时候魏国,向西灭掉秦国为战略纵深,然后向东争夺天下 可行吗
  • Docker MailServer自建邮件服务器
  • 【CRC校验】CRC(循环冗余校验)算法介绍
  • SpringAI 内嵌模型 ONNX
  • 哪些平台制作网站硬件开发和软件开发
  • 网站设计功能编程网站有哪些
  • Volatility2在kali安装
  • Euler
  • 提示学习思想
  • 《图解技术体系》Wonderful talk AI ~~AI“Emerging”
  • k8s部署容器化应用-nginx2
  • 谈谈你对iOS的runtime和runloop的了解
  • Blender入门学习05 - 材质
  • 沂源网站网站页面设计图片素材
  • 做网站推广引流效果好吗黑料社2023
  • 抽水蓄能电站的最佳调度方案研究Matlab仿真
  • VTK入门:vtkPolyData——3D几何的“乐高积木盒
  • php网站做退出的代码wordpress添加字体颜色
  • 2025年--Lc208- 415. 字符串相加(双指针)--Java版
  • ELK 日志管理系统相关内容总结
  • 如何使用 Ansible 安装 Docker
  • 图片批量压缩工具,快速减小文件体积
  • 安卓 ContentProvider 详解:跨应用数据共享的核心方案