最小生成树Prim算法

本文详细介绍了Prim算法的工作原理及其用于寻找图的最小生成树的过程。通过类比Dijkstra算法,阐述了Prim算法如何逐步构建一棵包含所有顶点的树,并确保树的总权重最小。文章还提供了具体的代码实现。

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

       Prim算法的执行非常类似于寻找图的最短通路的Dijkstra算法。Prim算法的特点是集合A中的边总是只形成单棵树。如图5所示,阴影覆盖的边属于正在生成的树,树中的结点为黑色。在算法的每一步,树中的结点确定了图的一个割,并且通过该割的轻边被加进树中。树从任意根结点r开始形成并逐渐生长直至该树跨越了V中的所有结点。在每一步,连接A中某结点到V-A中某结点的轻边被加入到树中,由推论2,该规则仅加大对A安全的边,因此当算法终止时,A中的边就成为一棵最小生成树。因为每次添加到树中的边都是使树的权尽可能小的边,因此上述策略也是贪心的。

有效实现Prim算法的关键是设法较容易地选择一条新的边添加到由A的边所形成的树中,在下面的伪代码中,算法的输入是连通图G和将生成的最小生成树的根r。在算法执行过程中,不在树中的所有结点都驻留于优先级基于key域的队列Q中。对每个结点v,key[v]是连接v到树中结点的边所具有的最小权值;按常规,若不存在这样的边则key[v]=∞。域p[v]说明树中v的“父母”。在算法执行中,GENERIC-MST的集合A隐含地满足:

A={(v,p[v])|vÎV-{r}-Q}

当算法终止时,优先队列Q为空,因此G的最小生成树A满足:

A={(v,p[v])|vÎ V-{r}}



代码如下:

#include<iostream>
#include<iomanip>
#include<list>
using namespace std;

#define UNDEFINE -1

//此处Prim算法的图为无向图

struct Edge
{
	int verno;			//邻接数组中节点编号
	int weight;			//权值
	Edge* next;			//指向下一条边
};

struct Vertex
{
	Edge *adj;			//所指向的节点所在边
	int verno;			//邻接数组中节点编号
	char key;			//关键字
};

struct Graph
{
	Vertex *vertexs;	//节点数组
	int vertexnum;		//节点个数
	int adjnum;			//边数
};

struct PrimEdge
{
	char startpoint;
	char endpoint;
	int weight;
	PrimEdge(char _pointx,char _pointy,int _wei) : startpoint(_pointx) , endpoint(_pointy),weight(_wei){};
};

class MSTPrim
{
public:
	MSTPrim(char *vertex,int vernum,char adj[][2],int *weight,int adjnum);
	void PrimInsert(int source,int dest,int weight);
	int PrimFindKey(char key);
	int PrimExtractMin(int *key,bool *visited,int length);
	void PrimSpanTree();
	void PrimSpanTreeOutput();
private:
	list<PrimEdge> primedge;
	Graph *primGraph;
};

MSTPrim::MSTPrim(char *vertex,int vernum,char adj[][2],int *weight,int adjnum)
{
	int i,source,dest;

	primGraph = new Graph;
	primGraph->vertexs = new Vertex[vernum];
	primGraph->adjnum = adjnum;
	primGraph->vertexnum = vernum;
	for(i = 0;i < vernum;i++)
	{
		primGraph->vertexs[i].key = vertex[i];
		primGraph->vertexs[i].verno = i;
		primGraph->vertexs[i].adj = NULL;
	}

	for(i = 0;i < adjnum;i++)
	{
		source = PrimFindKey(adj[i][0]);
		dest = PrimFindKey(adj[i][1]);
		PrimInsert(source,dest,weight[i]);
		PrimInsert(dest,source,weight[i]);			//无向图与有向图的区别在此
	}
}

void MSTPrim::PrimInsert(int source,int dest,int weight)
{
	if(primGraph->vertexs[source].adj == NULL || primGraph->vertexs[source].adj->weight > weight)
	{
		Edge* newnode = new Edge;
		newnode->verno = dest;
		newnode->weight = weight;
		newnode->next = primGraph->vertexs[source].adj;
		primGraph->vertexs[source].adj = newnode;
	}
	else
	{
		Edge* temp = primGraph->vertexs[source].adj;
		while(temp->next != NULL)						//插入新边的时候,把权值从低到高进行排序
		{
			if(temp->next->weight > weight)
				break;
			temp = temp->next;
		}
		Edge* newnode = new Edge;
		newnode->verno = dest;
		newnode->weight = weight;
		newnode->next = temp->next;
		temp->next = newnode;
	}
}

int MSTPrim::PrimFindKey(char key)
{
	int i;
	for(i = 0;i < primGraph->vertexnum;i++)
	{
		if(primGraph->vertexs[i].key == key)
			break;
	}
	return i;
}

int MSTPrim::PrimExtractMin(int *key,bool *visited,int length)
{
	int i,minno;

	for(i = 0; i < length;i++)		//先找到第一个没被访问的节点
	{
		if((visited[i] == false)&&(key[i] != UNDEFINE))
			break;
	}
	minno = i;
	for(i = minno + 1;i < length;i++)
	{
		if((visited[i] == false)&&(key[i] != UNDEFINE)&&(key[i] < key[minno]))
			minno = i;
	}
	return minno;
}

void MSTPrim::PrimSpanTree()
{
	Edge *temp;
	//int weight,starege,endege;			//用来记录新增边的开始点结束点与权重
	int i,next;
	int length = primGraph->vertexnum;
	bool *visited = new bool[length];
	int *key = new int[length];			//key[v]是所有将v与树中某顶点相连的边中的最小权值
	for(i = 0;i < length;i++)
	{
		visited[i] = false;
		key[i] = UNDEFINE;
	}

	key[0] = 0;													//初始化为0,感觉和下面都可以
	//key[0] = primGraph->vertexs[0].adj->weight;				//初始化第一个数值
	//visited[0] = true;
	for(i = 0;i < length;i++)
	{
		next = PrimExtractMin(key,visited,length);
		temp = primGraph->vertexs[next].adj;
		while(temp != NULL)
		{
			if(visited[temp->verno] == false)
			{
				if(((key[temp->verno] > temp->weight)&&(key[temp->verno] != UNDEFINE)) || (key[temp->verno] == UNDEFINE))
					key[temp->verno] = temp->weight;
			}
			else if(visited[next] == false)			//由于邻接链表是按照权重从小到达进行排序的,第一个存在边且被访问的节点一定是离已存在节点中最近的边
			{
				primedge.push_back(*(new PrimEdge(primGraph->vertexs[temp->verno].key,primGraph->vertexs[next].key,temp->weight)));
				visited[next] = true;
			}
			temp = temp->next;
		}
		visited[next] = true;
	}
}

void MSTPrim::PrimSpanTreeOutput()
{
	cout<<"The edges of PrimSpanTree :"<<endl;
	for(list<PrimEdge>::iterator iter = primedge.begin();iter != primedge.end();iter++)
	{
		cout<<setw(6)<<iter->startpoint<<setw(6)<<iter->endpoint<<setw(6)<<iter->weight<<endl;
	}
}

int main()
{
	char vertex[] = {'A','B','C','D','E','F','G','H','I'};
	int vernum = 9;
	char adj[][2] = {{'A','B'},{'A','H'},{'B','C'},{'B','H'},{'C','D'},{'C','I'},{'C','F'},{'H','I'},{'H','G'},{'G','I'},{'F','G'},{'D','F'},{'D','E'},{'E','F'}};
	int weight[] = {4,8,8,11,7,2,4,7,1,6,2,14,9,10};
	int adjnum = 14;
	MSTPrim *prim = new MSTPrim(vertex,vernum,adj,weight,adjnum);
	prim->PrimSpanTree();
	prim->PrimSpanTreeOutput();

	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值