Prim算法

本文详细介绍了最小生成树的Prim算法的核心思想、构造过程和应用实例,通过实例代码展示了如何利用Prim算法求解最小生成树问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

边赋以权值的图称为网或带权图带权图的生成树也是带权的生成树T各边的权值总和称为该树的权。

   最小生成树(MST):权值最小的生成树。

   生成树和最小生成树的应用:要连通n个城市需要n-1条边线路。可以把边上的权值解释为线路的造价。则最小生成树表示使其造价最小的生成树。

   构造网的最小生成树必须解决下面两个问题:

    1、尽可能选取权值小的边,但不能构成回路

    2、选取n-1条恰当的边以连通n个顶点

    MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。 


基本思想:

假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:

   在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。

   此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。

   Prim算法的核心:始终保持TE中的边集构成一棵生成树

看图说话:

下图展示了Prim算法查找的全过程



算法如下:

#include "GraphLink.h"

class Dist
{
public:
	int index; //顶点的索引值,仅Dijkstra算法会用到
	int length;//顶点之间的距离
	int pre;//路径最后经过的顶点

	bool operator < (const Dist &dist)
	{
		return length < dist.length;
	}

	bool operator <= (const Dist &dist)
	{
		return length <= dist.length;
	}

	bool operator > (const Dist &dist)
	{
		return length > dist.length;
	}

	bool operator >= (const Dist &dist)
	{
		return length >= dist.length;
	}

	bool operator == (const Dist &dist)
	{
		return length == dist.length;
	}
};

void addEdgeToMST(Edge &edge,Edge* &MST,int n)// 将边e加到MST中
{
	MST[n] = edge;
}

int minVertex(Graph &G,Dist * &D)// 在Dist数组中找最小值

{
	int v;
	for(int i = 0; i < G.verticesNum();i++)
	{
		if(G.mark[i] == UNVISITED)// 让v为随意一个未访问的定义

		{
			v = i;
			break;
		}
	}

	for(int i = 0; i < G.verticesNum();i++)
	{
		if(G.mark[i] == UNVISITED && D[i] < D[v])
		{
			v = i;// 保存目前发现的具有最小距离的顶点
		}
	}

	return v;
}

//最小支撑树的Prim算法,
//参数G是图,参数s是开始顶点,参数MST是保存最小支撑树中所有边的数组
void prim(Graph& G, int s, Edge* &MST)
{
	int MSTTag = 0;// 最小生成树边的标号
	MST = new Edge[G.verticesNum() -1];

	Dist *D = new Dist[G.verticesNum()];// 初始化Mark数组、D数组
	for(int i = 0; i < G.verticesNum(); i++)
	{
		G.mark[i] = UNVISITED;
		D[i].length = INFINITE;
		D[i].index = i;
		D[i].pre = s;
	}

	D[s].length = 0;
	G.mark[s] = VISITED;
	int v = s;
	for(int i = 0; i < G.verticesNum() -1; i++)
	{
		if(D[v].length == INFINITE)// 非连通,有不可达顶点,
		{
			break;
		}
		// 因为v的加入,需要刷新v邻接点的D值
		for(Edge edge = G.firstEdge(v); G.isEdge(edge); edge = G.nextEdge(edge))
		{
			if(G.mark[G.toVertex(edge)] == UNVISITED && D[G.toVertex(edge)].length > edge.weight)
			{
				D[G.toVertex(edge)].length = edge.weight;
				D[G.toVertex(edge)].pre = v;
			}
		}

		v = minVertex(G,D);
		G.mark[v] = VISITED;

		Edge edge(D[v].length,D[v].index,D[v].pre);
		addEdgeToMST(edge,MST,MSTTag++);
	}
}



int A[N][N] =  {
//   v0  v1  v2  v3  v4  v5  v6     
	0, 20,  0,  0,  1,  0,  0,
	20, 0,  6,  4,  0,  0,  0,
	0,  6,  0,  0,  0,  0,  2,
	0,  4,  0,  0,  0, 12,  8,
	1,  0,  0,  0,  0, 15,  0,
	0,  0,  0, 12, 15,  0, 10,
	0,  0,  2,  8,  0, 10,  0

};

int main()
{
	GraphLink<ListUnit> graphLink(N);     // 建立图 
	graphLink.initGraph(graphLink, A,N); // 初始化图

	Edge *D;
	prim(graphLink, 0, D);
	for (int i = 0; i < N - 1; i ++)
	{
		cout << "V" << D[i].from << "->V" << D[i].to << "   Weight is : " << D[i].weight << endl;

	}
	system("pause");
	return 0;
}

运行结果:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值