C++——算法基础之最小生成树(Prim 和 Kruskal)

本文介绍了两种求解最小生成树的经典算法:Prim算法和Kruskal算法。Prim算法以顶点为基础向外扩张,适合稠密图;Kruskal算法以边为基础向内收拢,适用于稀疏图。通过图例详细解释了两种算法的具体实现过程。

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


今天,大白跟大家分享一下最小生成树算法。


最小生成树算法有两种:Prim算法 和 Kruskal算法


Prim算法:以顶点为基础的向外扩张,从某一顶点(A)开始,依次寻找未知顶点中(未加入最小生成树的顶点)到已知顶点的最小权值的顶点。直到找到包含(A)的最大连通图中的所有顶点为止。(由此可见,从不同的顶点开始,获得的最小生成树也就不同,当把图中的每个顶点都建立最小生成树后,就获得了所谓的生成森林)。

所以,其适用于稠密图(顶点少于边)。


图例说明不可选可选已选(Vnew
 

此为原始的加权连通图。每条边一侧的数字代表其权值。 - - -

顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 C, G A, B, E, F D
 

下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。 C, G B, E, F A, D
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 C B, E, G A, D, F
 

在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。 C, E, G A, D, F, B
 

这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。 C, G A, D, F, B, E

顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG G A, D, F, B, E, C

现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 A, D, F, B, E, C, G
(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)


Kruskal算法:以边为基础的向内收拢,首先将所有边进行升序排序,然后从最小边开始,判断当前边的顶点是否已经在最小生成树中,若是,则舍弃该边继续,否则,将该边加入到最小生成树中。直到所有边都遍历完成。(在判断时,用到一个并查集)。(我暂时获得的是生成森林喔*_*)

其适用于稀疏图(即边较少)。


图例描述:

首先第一步,我们有一张图Graph,有若干点和边 

 

将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了右图

 

 

 

在剩下的变中寻找。我们找到了CE。这里边的权重也是5

依次类推我们找到了6,7,7,即DF,AB,BE。

下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。

最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是右:

 

(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)


代码如下:

Prim:

#include <iostream>
using namespace std;

#define MaxNum 20
#define MaxEdge 190
#define MAX 32767

struct MGraph
{
	char Vertex[MaxNum];
	int Edge[MaxNum][MaxNum];
	int n, e;
};

struct closedge			//辅助数组,存储从集合U到集合V-U中各顶点的权值最小边
{
	int adjvex;
	int lowcost;
}closedge[MaxNum];


int locate_vex (MGraph *G, char ver)
{
	for(int i = 0; i < G->n; i++)
	{
		if(G->Vertex[i] == ver)
		{
			return i;
		}
	}
	return -1;
}

void creatgraph (MGraph *G)
{
	int i, j, k, cost;
	char vex1, vex2;
	cout << "请输入网的顶点名称:";
	for(i = 0; i < G->n; i++)
	{
		cin >> G->Vertex[i];
	}
	for(i = 0; i < G->n; i++)
	{
		for(j = 0; j < G->n; j++)
		{
			G->Edge[i][j] = MAX;
		}
	}
	cout << "请输入网中每条边所依附的顶点信息:";
	for(k = 0; k < G->e; k++)
	{
		cout << "\n请输入第 " << k + 1 << " 条边的第一个顶点信息:";
		cin >> vex1;
		cout << "请输入第 " << k + 1 << " 条边的第二个顶点信息:";
		cin >> vex2;
		i = locate_vex (G, vex1);
		j = locate_vex (G, vex2);
		cout << "请输入第 " << k + 1 << " 条边的权值:";
		cin >> cost;
		G->Edge[i][j] = G->Edge[j][i] = cost;
	}
}

void Prim (MGraph *G)
{
	int i, j, k;
	int mincost;
	char vex;
	cout << "\n请输入构造最小生成树的起始顶点:";
	cin >> vex;
	k = locate_vex (G, vex);
	cout << "其余顶点为:";
	for(i = 0; i < G->n; i++)
	{
		if(i != k)
		{
			closedge[i].adjvex = k;
			closedge[i].lowcost = G->Edge[k][i];
		}
	}
	closedge[k].lowcost = 0;
	closedge[k].adjvex = k;
	for(i = 0; i < G->n - 1; i++)
	{
		mincost = MAX;
		j = 0;
		k = 0;
		while(j < G->n)
		{
			if(closedge[j].lowcost < mincost && closedge[j].lowcost != 0)
			{
				mincost = closedge[j].lowcost;
				k = j;
			}
			j++;
		}
		cout << "\n顶点 " << G->Vertex[k] << " 边的权值为:" << mincost;
		closedge[k].lowcost = 0;
		for(j = 0; j < G->n; j++)
		{
			if(G->Edge[k][j] < closedge[j].lowcost)
			{
				closedge[j].lowcost = G->Edge[k][j];
				closedge[j].adjvex = k;
			}
		}
	}
}

void outputgraph (MGraph *G)
{
	for(int i = 0; i < G->n; i++)
	{
		cout << "\t" << G->Vertex[i];
	}
	cout << endl << endl;
	for(int i = 0; i < G->n; i++)
	{
		cout << G->Vertex[i];
		for(int j = 0; j < G->n; j++)
		{
			if(G->Edge[i][j] == MAX)
			{
				cout << "\t" << "∞";
			}
			else
			{
				cout << "\t" << G->Edge[i][j];
			}
		}
		cout << endl << endl;
	}
}

int main28 ()
{
	MGraph mg;
	cout << "以 Prim 算法构造无向连通网的最小生成树......";
	cout << "\n请输入连通网的顶点数目和边数,以空格‘ ’分隔开:";
	cin >> mg.n >> mg.e;
	creatgraph (&mg);
	outputgraph (&mg);
	Prim (&mg);
	return 0;
}

Kruskal:

#include <iostream>
using namespace std;

#define INF 10000
#define MAX 20
#define MAXE 100

struct MyGraph
{
	int vNum;
	int eNum;
	int vexs[MAX];
	int edges[MAX][MAX];
};

struct edgeArray
{
	int v1, v2, w;
};

edgeArray edgearr[MAX];

int getVerIndex (int ver)
{
	return ver - 1;
}

void createMygGraph (MyGraph *mg)
{
	cout << "请输入图的顶点数和边数:";
	cin >> mg->vNum >> mg->eNum;
	cout << "顶点:";
	for(int i = 0; i < mg->vNum; i++)
	{
		cin >> mg->vexs[i];
	}
	for(int i = 0; i < mg->vNum; i++)
	{
		for(int j = 0; j < mg->vNum; j++)
		{
			mg->edges[i][j] = INF;
		}
	}
	int v1, v2, w;
	for(int i = 0; i < mg->eNum; i++)
	{
		cout << "边" << i + 1 << " 的顶点1,顶点2,权值:";
		cin >> v1 >> v2 >> w;
		int index1 = getVerIndex (v1);
		int index2 = getVerIndex (v2);
		mg->edges[index1][index2] = mg->edges[index2][index1] = w;
		edgearr[i].v1 = v1;
		edgearr[i].v2 = v2;
		edgearr[i].w = w;
	}
}

void sortEdge (MyGraph * mg)
{
	for(int i = 1; i < mg->eNum; i++)
	{
		bool flag = true;
		for(int j = 0; j < mg->eNum - i; j++)
		{
			if(edgearr[j].w > edgearr[j + 1].w)
			{
				edgeArray temp;
				temp.v1 = edgearr[j].v1;
				temp.v2 = edgearr[j].v2;
				temp.w = edgearr[j].w;

				edgearr[j].v1 = edgearr[j + 1].v1;
				edgearr[j].v2 = edgearr[j + 1].v2;
				edgearr[j].w = edgearr[j + 1].w;

				edgearr[j + 1].v1 = temp.v1;
				edgearr[j + 1].v2 = temp.v2;
				edgearr[j + 1].w = temp.w;

				flag = false;
			}
		}
		if(flag)
		{
			break;
		}
	}
}

int father[MAX];

int find (int x)
{
	if(x != father[x])
	{
		father[x] = find (father[x]);
	}
	return father[x];
}

void Kruskal (MyGraph *mg)
{
	cout << "最小生成树为:\n";
	int j = 0;
	for(int k = 0; k < mg->vNum; k++)
	{
		father[k] = k;
	}
	while(j < mg->eNum)
	{
		int v1 = edgearr[j].v1;
		int v2 = edgearr[j].v2;
		v1 = find (v1);
		v2 = find (v2);
		if(v1 != v2)
		{
			cout << "<" << edgearr[j].v1 << "," << edgearr[j].v2 << "> : " << edgearr[j].w << "\t";
			father[v1] = v2;
		}
		j++;
	}
	cout << endl;
}

void printGraph (MyGraph *mg)
{
	for(int i = 0; i < mg->vNum; i++)
	{
		cout << "\t" << mg->vexs[i];
	}
	cout << endl << endl;
	for(int i = 0; i < mg->vNum; i++)
	{
		cout << mg->vexs[i];
		for(int j = 0; j < mg->vNum; j++)
		{
			if(INF == mg->edges[i][j])
			{
				cout << '\t' << "∞";
			}
			else
			{
				cout << '\t' << mg->edges[i][j];
			}
		}
		cout << endl << endl;
	}
}

int main()
{
	MyGraph mg;
	createMygGraph (&mg);
	printGraph (&mg);
	sortEdge (&mg);
	Kruskal (&mg);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值