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

概况

最小生成树针对的是无向图!它是原图的极小连通子图,是最小权重生成树的简称。其特点是:图中包含原图中的所有 n 个结点,并且有保持图连通的最少的边。

求最小生成树有两种算法,分别是:Kruskal(克鲁斯卡尔)算法、Prim(普里姆)算法。

本文将介绍笔者对Prim算法的理解。Kruskal算法将在后续文章介绍。

Prim算法

该算法需要用邻接矩阵存储图(使用edge数组记录边的权值,vertex数组存储顶点数据)。

class MGraph
{
public:
   	MGraph(DataType a[ ], int n, int e);     //构造函数——建图(建立n个顶点e条边的图)
   	~MGraph( ){ };                        //析构函数
 	void Prim(int v);
private:
    DataType vertex[MaxSize];           //存放图中顶点的数组
    int edge[MaxSize][MaxSize];           //存放图中边的数组
    int vertexNum, edgeNum;              //图的顶点数和边数
 	int MinEdge(int r[ ], int n);
};

图的构造

存储图的基本信息(边、点、代价)

MGraph :: MGraph(char a[ ], int n, int e) 
{
   	int i, j, k, w;
   	vertexNum = n; edgeNum = e;
   	for (i = 0; i < vertexNum; i++)          //存储顶点
   		vertex[i] = a[i];
   	for (i = 0; i < vertexNum; i++)          
        //初始化邻接矩阵:初始化权值(赋很大的数值即可)
 		for (j = 0; j < vertexNum; j++)
   			if (i == j)
			   edge[i][j] = 0;	//顶点自己到自己不需要代价
			else
			   edge[i][j] = 100;                          
   	for (k = 0; k < edgeNum; k++)           //依次输入每一条边
   	{
   		cout << "请输入边依附的两个顶点的编号,以及边上的权值:";
		cin >> i >> j >> w;                       //输入边依附的两个顶点的编号
		edge[i][j] = w; edge[j][i] = w;           //置有边标志
   	}
}

核心代码

prim算法的思想是加点法,即将图中顶点(V)分两部分,最小生成树的点集为U,其余顶点在集合(V-U)中。加点过程中,需要使用数组adjvex记录上一轮找到的最小值的位置(也就是连接当前最小值点的权值最小边另一端顶点),使用cost数组记录到各顶点的代价

简言之,每轮迭代都可以找到到达新加入U中顶点代价最短的边及该边另一端的顶点,输出内容均为当前最短边两端的顶点及这条最短边的权值!

基本思路

1.初始化

将起点的邻接矩阵录入cost中,将所有加点位置(adjvex)置为起点。

2.从起点开始对剩余的(n-1)个点迭代

每轮迭代过程中,首先需要在cost数组内寻找最小值,并输出本轮最小值到上一轮最小值之间的代价,同时将本轮最小值位置的数据清0,以防止之后的遍历重复。【从起点出发花最少代价可到的点加入U】

之后再次找最小权值,并更新cost数组,同时在adj数组中记录上一轮迭代得到最小值的位置【记录新加入U中的点来源】

代码实现

找最小值的位置
int FindMin(int r[],int n){
	int index;
	int min=100;			//图中所有权值最大不超过100 
	for(int i=0;i<n;i++)
	if(r[i]!=0 &&r[i]<min){
		min=r[i];
		index=i;
	}	
	return index;			//返回最小值在数组中的位置 
}
Prim核心代码
void Prim(int v){
	int adjvex[MaxSize],cost[MaxSize];
	int i,j,k;
						//通过起点对adj、cost数组初始化 
	for(i=0;i<vertxNum;i++){
		cost[i]=edge[v][i];
		adjvex[i]=v;
						//将起点的邻接矩阵录入cost中,将所有加点位置(adjvex)置为起点。
	}
	cost[v] = 0;		//将顶点加入U中
	for(k=1;k<vertxNum;k++) {
		j=FindMin(cost,vertxNum);		//在cost数组内寻找最小值
		cout<<"("<<vertx[j]<<","<<vertx[ adjvex[j] ]<<")" <<cost[j]<<endl;	//输出本轮最小值到上一轮最小值之间的代价
		cost[j]=0;						//将最小值点加入U中(将到达当前最小值位置的代价清0,也有防止之后重复遍历的作用)
		
		for(int p=0;p<vertxNum;p++){	 
			if(edge[p][j]<cost[p]){		//从所有与当前最小值邻接点出发找到最小值点代价最小的 
				cost=edge[p][j];
				adjvex[p]=j;			//记录新加入顶点前上一轮迭代得到的最小值位置 
			}
		} 
	}
	
} 

实例

在这里插入图片描述

如上图,求其最小生成树。

代码

#include<iostream>
using namespace std;
const int MaxSize=100;
class MGraph
{
public:
   	MGraph(char a[ ], int n, int e);     //构造函数——建图(建立n个顶点e条边的图)
   	~MGraph( ){ };                        	//析构函数(图使用静态存储,无需手动析构!) 
 	void Prim(int v);				 
private:
    char vertex[MaxSize];          		//存放图的顶点
    int edge[MaxSize][MaxSize];           	//邻接矩阵
    int vertxNum, edgeNum;              	//图的顶点数和边数
};
MGraph :: MGraph(char a[ ], int n, int e) 
{
   	int i, j, k, w;
   	vertxNum = n; edgeNum = e;
   	for (i = 0; i < vertxNum; i++)          //存储顶点
   		vertex[i] = a[i];
   	for (i = 0; i < vertxNum; i++)          
        //初始化邻接矩阵:初始化权值(赋很大的数值即可)
 		for (j = 0; j < vertxNum; j++)
   			if (i == j)
			   edge[i][j] = 0;				//顶点自己到自己不需要代价
			else
			   edge[i][j] = 100;                          
   	for (k = 0; k < edgeNum; k++)           //依次输入每一条边
   	{
   		cout << "输入边依附的两个顶点的编号,以及边上的权值:";
		cin >> i >> j >> w;                       //输入边依附的两个顶点的编号
		edge[i][j] = w; edge[j][i] = w;           //置有边标志
   	}
}

int FindMin(int r[],int n){
	int index;
	int min=100;			//图中所有权值最大不超过100 
	for(int i=0;i<n;i++)
	if(r[i]!=0 &&r[i]<min){
		min=r[i];
		index=i;
	}	
	return index;			//返回最小值在数组中的位置 
}

void MGraph :: Prim(int v){
	int adjvex[MaxSize],cost[MaxSize];
	int i,j,k;
						//通过起点对adj、cost数组初始化 
	for(i=0;i<vertxNum;i++){
		cost[i]=edge[v][i];
		adjvex[i]=v;
						//将起点的邻接矩阵录入cost中,将所有加点位置(adjvex)置为起点。
	}
	
	for(k=1;k<vertxNum;k++) {
		j=FindMin(cost,vertxNum);		//在cost数组内寻找最小值
		cout<<"("<<vertex[j]<<","<<vertex[ adjvex[j] ]<<")" <<cost[j]<<endl;	//输出本轮最小值到上一轮最小值之间的代价
		cost[j]=0;						//为防止之后的遍历重复,将本轮最小值位置的数据清0
		
		for(int p=0;p<vertxNum;p++){	 
			if(edge[p][j]<cost[p]){		//从所有与当前最小值邻接点出发找到最小值点代价最小的 
				cost[p]=edge[p][j];
				adjvex[p]=j;			//记录新加入点之前上一轮迭代中最小值的位置 
			}
		} 
	}
} 

int main( )
{
	/*测试数据为带权无向图, 输入边依次为 : 
	(0 1 34)(0 2 46)(0 5 19)(1 4 12)(2 3 17)(2 5 25)(3 4 38)(3 5 25)(4 5 26) */ 
	char ch[ ]={'A','B','C','D','E','F'};       
	MGraph MG(ch, 6, 9);             
	MG.Prim(0);
	return 0;
}

运行结果

在这里插入图片描述

即最小生成树为:
在这里插入图片描述

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值