数据结构/图论:最小生成树问题

数据结构/图论:最小生成树问题

一、问题描述

图的生成树是一个包含所有顶点的子图并且是一棵树。一个图可能有很多生成树。生成树问题是网络理论解决的典型问题之一,应用领域非常多。
应用:使连接电路板上一系列接头所需焊接的线路最短;使在几个城市间建立电话网所需的线路最短。

二、算法实现

1、Prim 算法

算法思想:
Prim算法是一个贪心算法。取图中任意一个顶点v作为生成树的根,之后往生成树上添加新的顶点w。在添加的顶点w和已经在生成树上的顶点v之间必定存在一条边,并且该边的权值在所有连通顶点v和w之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有n个顶点为止。

伪代码实现:

void Prim(Graph<string>* G, int* D, int s)
{
	// Prim's MSTalgorithm构造最小生成树算法
	int V[G->n()];// Store closest vertex 存储最近节点
	int i, w;
	for (i=0;i<G->n(); i++)
	{// Process the vertices 处理节点
	int v =minVertex(G, D);
	G->setMark(v, VISITED);
	if (v != s)
		AddEdgetoMST(G,V[v], v);// Add edge to MST 向MST中添加边
	if (D[v] ==INFINITY) return;// Unreachable vertices 不可达节点
	for(w=G->first(v); w<G->n(); w = G->next(v,w))
	if (D[w]> G->weight(v,w))
		{D[w] =G->weight(v,w);// Update distance 更新距离
		V[w] =v;// Where it came from记录节点
		}
	}
}

minVertex函数:

// Find min cost vertex寻找未访问的最短距离顶点
int minVertex(Graph* G,int* D)
{
	int i,V;
	// Set v to an unvisited vertex
	for(i=0;i<G->n();i++)
	if(G->getMark(i)==UNVISITED)
	{v=i;break;}
	// Now find smallest D va lue
	for(i++;i<G->n0;i++)
	if((G->getMark(i)==UNVISITED)&&(i<D[v1]))
	v=i;
	return V;
}

优先队列实现的Prim 算法:

void Prim(Graph* G, int* D, int s)
{
	int i, v,w; // The current vertex
	int V[G-n()]; // Who's closest
	DijkElem temp;
	DijkElem E[G->e()]; // Heap array with lots of space 
	temp.distance = 0;
	temp.vertex = s;
	E[0] = temp; // Initialize heap array
	minheap< DijkElem,DDComp> H(E, 1, G->e()); // Create the heap
	for (i=0; i<G->n(); i++)
	{// Now build MST
		do
		{if (!H.removemin(temp)) return;
		v = temp.vertex;
		} while (G.getMark(v) == VISITED);
	G->setMark(v, VISITED);
	if (v != s) AddEdgetoMST(V[v], v);
	if (D[v] == INFINITY) return; // Remaining vertices
	for (w = G->first(v); w<G->n(); w = G->next(v,w))
	if (D[w] > G->weight(v,w))
		{D[w] = G->weight(v,w);
		V[w] = v; temp.distance = D[w];
		temp.vertex = w;
		H.insert(temp); // Insert new distance in heap
		}
	}
}

注意:适用于稠密图。

2、Kruskal 算法

算法思想:Kruskal算法也是一个简单的贪心算。首先,将顶点集分为|V|个等价类,每个等价类包括一个顶点。然后,以权的大小为序处理各条边。如果某条边连接两个不同等价类的顶点,则这条边 被加入MST,两个等价类也被合为一个。反复执行此过程直至只余下一个等价类。

伪代码实现:

Class KruskElem
{
Public:
	int from,to,distance;
	KruskElem()
	{from=to=distance=-1;}
	KreskElem(int f,int t,int d)
	{from=f;to=t;distance=d;}
};
Kruskel(Graph* G)
{
	Gentree A(G->n());
	KruskElem E[G->e()];
	int I;
	int edgecnt=0;
	for (i=0;i<G->n();i++)
	for (int w=G->first(I);w<G->weight(I,w))
	{E[edgecnt].distance=G->weight(i,w);
	E[edgecnt].from=i;
	E[edgecnt++].to=w;
	}
	Minheap<KruskElem,KKComp>H(E,edgecnt,edgecnt);
	int numMST=G->n();
	for (i=0;numMST>1;i++)
	{KruskElem temp;
	H.removemin(temp);
	int v=temp.from;
	int u=temp.to;
	if (A.differ(v,u))
		{A.UNION(v,u);
		AddEdgetoMST(temp.from,temp.to);
		numMST--;
		}
	}
}

注意:适用于稀疏图。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

使君杭千秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值