阿空要找工作了系列之最小生成树

本文深入探讨了最小生成树(Minimum Spanning Tree)的概念及其两种核心算法:Prim算法和Kruskal算法。通过详细解释两种算法的工作原理,包括如何在稠密和稀疏图中选择合适的算法,为读者提供了全面的理解。

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

最小生成树-Minimum Spanning Tree

  • 一个重心:总权最小连通子图(遍历所有边和点)
  • 两个基本点:
    1. Prim:由点入手找边
    2. Kruskal:以边入手找点

Prim

Prim通过逐点遍历搜索,找与现有最小生成树(初态仅有一个点,然后逐渐生成)最近的一个点,不断填充来生成MST。

int prim(vector<vector<int>> graph)
{
	int pointnum = graph.size();
	vector<int> lowcost(pointnum);
	vector<int> mst(pointnum);
	int minid, min, sum = 0;
	for (int i = 0; i < pointnum; i++)
	{
		lowcost[i] = graph[0][i];
		mst[i] = 1;
	}
	mst[0] = 0;
	for (int i = 1; i < pointnum; i++)
	{
		minid = 0;
		min = INT32_MAX;
		for (int j = 1; j < pointnum; j++)
		{
			if (lowcost[j] < min && lowcost[j] != 0)
			{
				min = lowcost[j];
				minid = j;
			}
		}
		cout << "V" << mst[minid]+1 << "-V" << minid+1 << "=" << min << endl;
		sum += min;
		mst[minid] = 0;
		lowcost[minid] = 0;
		for (int j = 1; j < pointnum; j++)
		{
			if (lowcost[j] > graph[minid][j] && lowcost[j] != 0)
			{
				lowcost[j] = graph[minid][j];
				mst[j] = minid;
			}
		}
		
	}
	return sum;
}

int main()
{
	vector<vector<int>> weight = {
		vector<int>{1,2,6},
		vector<int>{1,3,1},
		vector<int>{1,4,5},
		vector<int>{2,3,5},
		vector<int>{2,5,3},
		vector<int>{3,4,5},
		vector<int>{3,5,6},
		vector<int>{3,6,4},
		vector<int>{4,6,2},
		vector<int>{5,6,6},
	};
	int pointnum = 6;
	int edgenum = 10;
	//初始化图G,权填充为最大值
	vector<vector<int>> graph(pointnum);
	for (int i = 0; i < pointnum; i++)
	{
		graph[i].resize(pointnum);
		for (int j = 0; j < pointnum; j++)
		{
			graph[i][j] = INT32_MAX;
		}
	}
	//构建图G
	for (int k = 0; k < weight.size(); k++)
	{
		int i = weight[k][0]-1;
		int j = weight[k][1]-1;
		graph[i][j] = weight[k][2];
		graph[j][i] = weight[k][2];
	}
	int cost = prim(graph);
	return 0;
}

Kruskal

Kruskal通过找最短的不构成回路的边来生成树,算法实现有一点比较困扰的是迭代寻找路径起点,点明了之后就不难理解。

struct edge
{
	//起点,终点,权
	int fr, to, w;
	bool operator < (const edge &a) const {
		return w<a.w;
	}
};
vector<edge> e;
vector<int> pre;
vector<edge> mst;
//赋值
void add(int fr, int to, int w)
{
	edge tempedge;
	tempedge.fr = fr;
	tempedge.to = to;
	tempedge.w = w;
	e.push_back(tempedge);
}
//寻找路径起点
int fin(int x)
{
	if (x == pre[x])
		return x;
	return pre[x] = fin(pre[x]);
}
void Kruskal()
{
	int n = 6;
	pre.resize(n + 1);
	for (int i = 1; i <= n; ++i)
		pre[i] = i;
	sort(e.begin(), e.begin() +e.size() );
	int ans = 0;
	for (int i = 0; i<e.size(); ++i) {
		int u = fin(e[i].fr);
		int v = fin(e[i].to);
		//判断是否加入一条边后,生成树中的两点是否有相同的起点
		if (u != v) {
			mst.push_back(e[i]);
			ans += e[i].w;
			pre[u] = v;
		}
		
	}
	for (int k = 0; k < mst.size(); k++) {
		cout << "V" << mst[k].fr << "-V" << mst[k].to << "=" << mst[k].w<< endl;
	}
	printf("%d\n", ans);
}
int main()
{
	vector<vector<int>> weight = {
		vector<int>{1,2,6},
		vector<int>{1,3,1},
		vector<int>{1,4,5},
		//vector<int>{1,5,INT32_MAX},
		//vector<int>{1,5,INT32_MAX},
		vector<int>{2,3,5},
		//vector<int>{2,4,INT32_MAX},
		vector<int>{2,5,3},
		//vector<int>{2,6,INT32_MAX},
		vector<int>{3,4,5},
		vector<int>{3,5,6},
		vector<int>{3,6,4},
		//vector<int>{4,5,INT32_MAX},
		vector<int>{4,6,2},
		vector<int>{5,6,6},
	};	

	for (int k = 0; k < weight.size(); k++)
	{		
		add(weight[k][0], weight[k][1], weight[k][2]);
		add(weight[k][1], weight[k][0], weight[k][2]);
	}
	Kruskal();	
	return 0;
}

比较

由于算法复杂度的问题,在稠密连接的树中用Prim,稀疏的树中用Kruskal。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值