- 目的
- 了解最小生成树的概念
- 熟悉并掌握prim算法
- 要求
一幅图共有6个结点,其值为字符A~F,构成如下图所示网络,
图1.实验要求的图
编程实现以下功能:1、采用邻接矩阵进行存储,2、采用prim算法,从顶点A出发生成最小生成树,依次输出所有选取的边,比如选取权值为6的边时,输出(A,B)。
- 原理
1.最小代价生成树背景:假设在n个城市之间建立铁路网,在每两个城市之间都可设置一条线路,每条线路对应要付出的代价不同,n个城市之间最多可生成(n2-n)/2条线路,我们如何设计线路才能用最少的路径(最少要n-1条)最低的成本架设完这些铁路线路?
我们用最少的线路,即n-1条线路来贯穿n个城市,结果不唯一,付出的总代价也不唯一,我们将每一个结果称作生成树,而总花费最少的我们称作最小代价生成树(Minimum Cost Spanning Tree),简称最小生成树。
2.MST性质及理解
MST性质定义:设G=(V,E)是一个连通网,U是顶点集V的一个真子集。若(u,v)是G中一条具有最小权值的边,其中u∈U,v∈V-U,则一定存在G的一棵最小生成树包括此边(u,v)。
直白的意思就是说:整个图(网)G中权值最小的边一定会是最小生成树的边。
假设图G有3个顶点三条边,AB、AC和BC,权值分别为3、4、5,那么此时在这个简单图中的最小生成树肯定会包含权值为3的AB。因为A结点在选择AB或AC的时候会选权值小代价低的那个。其实这个MST也就是——贪心算法,选择当前情况下的最优解,即对应了MST中选择代价最小的。当顶点A在选择的时候,只能选择A-B或A-C,那么A-B的代价更小,故选择A-B。这就是MST性质。
图2.MST性质
3.Prim算法:Prim算法是一种用于解决最小生成树问题的贪心算法。最小生成树是指一个无向图的生成树(即包含所有顶点且边的权重之和最小的树)。Prim算法的基本思想是从一个起始顶点开始,逐步扩展生成树,每次选择权重最小的边连接一个新的顶点,直到生成树包含了图中的所有顶点。
Prim算法的时间复杂度为O(ElogV),其中V为顶点数,E为边数。它适用于稠密图,即边的数目接近V^2的情况。如果图是稀疏图,即边的数目接近V的情况,使用Kruskal算法可能更加高效。
图3.Prim算法图示
- 编码步骤和结果
使用邻接矩阵来表示图,其中graph[i][j]表示顶点i到j的边的权值;primMST函数实现了Prim算法的核心,然后使用printMST函数用于打印最小生成树的边;最后main函数中首先读入图的顶点数和邻接矩阵,然后调用primMST函数来计算最小生成树。
其中Prim算法的具体实现步骤如下:
1. 创建一个空的最小生成树集合,开始时只包含起始顶点。
2. 初始化一个优先队列,用于选择权重最小的边。
3. 从起始顶点开始,将所有与之相邻的边加入优先队列。
4. 重复以下步骤,直到最小生成树包含了图中的所有顶点:
- 从优先队列中选择权重最小的边,将其加入最小生成树集合。
- 将该边连接的顶点标记为已访问。
- 将该顶点的所有未访问的相邻边加入优先队列。
5. 最终得到的最小生成树就是原图的最小生成树。
图4.实验结果
- 编码中出现的问题及解决方法
问题:输出的边的表示是由数字0-5来表示而不是用字符A-F
解决方法:使用一个字符数组来存储顶点的标签,
char labels[] = {'A', 'B', 'C', 'D', 'E', 'F'};
然后修改printMST函数中对应的顶点的索引,就可解决这一问题
- 总结及体会
在完成Prim算法的代码编写后,我对算法的理解更加深入。通过将算法转化为C语言代码,我更加清晰地理解了Prim算法的核心思想和实现过程。在编写代码的过程中,我不仅学会了如何使用邻接矩阵来表示图,还掌握了如何使用数组和循环来实现算法的逻辑。此外,我还熟悉了如何通过硬编码读取数据,以及通过字符数组将顶点索引转换为对应的字符标签。通过这个练习,我提高了对Prim算法的理解,同时也增强了对C语言编程的熟练度和实际应用能力。
- 代码部分
#include <stdio.h>
#include <stdbool.h>
#define MAX_VERTICES 6
#define INF 1000000
int graph[MAX_VERTICES][MAX_VERTICES]={
{0,6,8,1000,1000,1000},
{6,0,1000,12,14,1000},
{8,1000,0,16,17,1000},
{1000,12,16,0,1000,15},
{1000,14,17,1000,0,5},
{1000,1000,1000,15,5,0}
}; // 图的邻接矩阵
int parent[MAX_VERTICES]; // 记录最小生成树的父节点
int key[MAX_VERTICES]; // 记录顶点与最小生成树的边的权值
bool mstSet[MAX_VERTICES]; // 记录顶点是否已经在最小生成树中
int minKey(int key[], bool mstSet[], int vertices) {
int min = INF, min_index;
for (int v = 0; v < vertices; v++) {
if (mstSet[v] == false && key[v] < min) {
min = key[v];
min_index = v;
}
}
return min_index;
}
void printMST(int vertices) {
printf("Edge \t Weight\n");
char labels[]={'A','B','C','D','E','F'};
for (int i = 1; i < vertices; i++) {
printf("(%c , %c) \t%d \n", labels[parent[i]], labels[i], graph[i][parent[i]]);
}
}
void primMST(int vertices) {
for (int i = 0; i < vertices; i++) {
key[i] = INF;
mstSet[i] = false;
}
key[0] = 0;
parent[0] = -1;
for (int count = 0; count < vertices - 1; count++) {
int u = minKey(key, mstSet, vertices);
mstSet[u] = true;
for (int v = 0; v < vertices; v++) {
if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}
printMST(vertices);
}
int main() {
int vertices=6;
primMST(vertices);
return 0;
}
#include <stdio.h>
#include <stdbool.h>
#define MAX_VERTICES 6
#define INF 1000000
int graph[MAX_VERTICES][MAX_VERTICES]={
{0,6,8,1000,1000,1000},
{6,0,1000,12,14,1000},
{8,1000,0,16,17,1000},
{1000,12,16,0,1000,15},
{1000,14,17,1000,0,5},
{1000,1000,1000,15,5,0}
}; // 图的邻接矩阵
int parent[MAX_VERTICES]; // 记录最小生成树的父节点
int key[MAX_VERTICES]; // 记录顶点与最小生成树的边的权值
bool mstSet[MAX_VERTICES]; // 记录顶点是否已经在最小生成树中
int minKey(int key[], bool mstSet[], int vertices) {
int min = INF, min_index;
for (int v = 0; v < vertices; v++) {
if (mstSet[v] == false && key[v] < min) {
min = key[v];
min_index = v;
}
}
return min_index;
}
void printMST(int vertices) {
printf("Edge \t Weight\n");
char labels[]={'A','B','C','D','E','F'};
for (int i = 1; i < vertices; i++) {
printf("(%c , %c) \t%d \n", labels[parent[i]], labels[i], graph[i][parent[i]]);
}
}
void primMST(int vertices) {
for (int i = 0; i < vertices; i++) {
key[i] = INF;
mstSet[i] = false;
}
key[0] = 0;
parent[0] = -1;
for (int count = 0; count < vertices - 1; count++) {
int u = minKey(key, mstSet, vertices);
mstSet[u] = true;
for (int v = 0; v < vertices; v++) {
if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}
printMST(vertices);
}
int main() {
int vertices=6;
primMST(vertices);
return 0;
}