最小生成树Kruskal、Prim算法C++

C++实现kruskal算法 C++实现kruskal算法 文章目录C++实现kruskal算法1,基本思想2,注意点3,代码实现 1,基本思想 假设共有n个节点,m条边,每次从未选择过的边集E中选取权值最小的一条边,若该最小边的端点不与已选边的端点构成回路,则将该边加入到最小生成树中,重复这一步骤直到选够n-1条边或无边可选。举例如下: 图中共有9条边, 第一次:选取最小一条边,权值为1,节点v1和v3被标注; 第二次:选取最小一条边,权值为2,节点v1和v4被标注; 第三次:选取最小一条边,权值为3,节点v1和v5被标注; 阅读详情

什么是最小生成树

连通图:

在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1和顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。

生成树:

一个连通图的最小连通子图称作为图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。

最小生成树:

最小生活树是生成树的一个特殊情况,它的边权之和最小。其特点如下:

  1. 只能使用图中权值最小的边来构造最小生成树
  2. 只能使用恰好n-1条边来连接图中的n个顶点
  3. 选用的n-1条边不能构成回路(构成回路会导致有顶点为连通或权值过大)

最小生成树的实际应用

例如城市道路铺设中,如果我们直接使用连通结构,这样两点间的交通必然是最便捷的。可是修路的成本的巨大的,但是又要连通所有城市,这便会想到使用最小生成树。

考虑到经济发达城市人口多、车辆多,我们还需要为其多修建一些道路,则实际中的道路修建与最小生成树的结果不同,但是最小生成树在这些实际场景发挥了很大作用。

Kruskal算法

任给一个有n个顶点的连通网络N = {V,E}。

首先构造一个由这n个顶点组成、不含任何边的图G={V,null},其中每个顶点自成一个连通分量,其次不断从E中取出权值最小的一条边(若有多条任取其中之一),若该边的两个顶点来自不同的连通分量,则将此边加入到G中。如此重复,知道所有顶点在同一个连通分量上为止。

算法思路

核心:每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树。

将权值小的边放入到优先级队列中。依次类推

接下来有一个问题,因为生成树不能构成回路,所以在添加边的时候要处理成环问题。

这个问题的解决就要使用并查集;如果该边已经出现过了,则不选择该边,如果该边不在集合中,则可以选择该边

以下是选边的全部过程:

代码实现

首先我们要创建一个边的数据结构Edge,用于将边存放到优先级队列中。

struct Edge
{
	size_t _srci;
	size_t _dsti;
	W _w;
	Edge(size_t srci, size_t dsti, W w)
		:_srci(srci), _dsti(dsti), _w(w)
	{
	}
	//还要提供一个比较函数,因为优先级队列中使用到了greater,greater会调用>函数
	bool operator > (const Edge& ed) const
	{
		return _w > ed._w;
	}
};

实现思路:

1. 将所有的边统计到优先级队列中,(注意临界矩阵只遍历一半)

2. 从优先级队列中选出n-1条边,n为顶点数量

3. 依次取出队列中的元素,判断该元素是否出现过(使用并查集)

4. 如果没有出现过,则添加该边到最小生成树中,并将该边添加到并查集中,再统计权值。

5. 当队列元素取空时,最小生成树中边的数量小于n-1,则表示该图没有连通,则没有最小生成树,直接返回权值的默认值即可。

6. 如果size等于n-1,则表示最小生成树创建成功,返回权值总和即可

//最小生成树
//如果有最小生成树,则返回该树的权值,如果没有最小生成树,则返回默认值
W Kruskal(Self& minTree)
{
	//初始化minTree
	size_t n = _vertexs.size();
	minTree._vertexs = _vertexs;
	minTree._indexMap = _indexMap;
	minTree._matrix = vector<vector<W>>(n, vector<W>(n, MAX_W));
	//1. 将边统计起来,使用优先级队列或排序的方式都可以   
	priority_queue<Edge, vector<Edge>, greater<Edge>> minque;

	for (size_t i = 0; i < n; i++)
	{
		//矩阵中走一半即可  要不然会重复入队列
		for (size_t j = 0; j < i; j++)
		{
			if (_matrix[i][j] != MAX_W)
			{
				minque.push(Edge(i, j, _matrix[i][j]));
			}
		}
	}
	//2.选出n-1条边
	size_t size = 0;
	UnionFindSet ufs(n);   //n个顶点
	W total_W{};            //总的权值
	while (!minque.empty() && size < n)
	{
		Edge min = minque.top();
		minque.pop();

		//3.选出一条边之后看该边在不在当前集合,
		//在就不选择该边,不在就选择该边,并标记
		if (!ufs.IsInset(min._srci, min._dsti))
		{
			cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] <<
				":" << _matrix[min._srci][min._dsti] << endl;
			minTree._AddEdge(min._srci, min._dsti, min._w);
			ufs.Union(min._srci, min._dsti);
			size++;
			total_W += min._w;
		}
	}
	//如果该图不是连通图,则没有最小生成树
	if (size < n - 1)
	{
		return W();
	}
	return total_W;
}

接下来是一些要注意的点:

1. 要将最小生成树进行初始化,否则添加边时会出现越界问题。

2. 最小生成树其实就是该图的子图,typedef Graph<V, W, MAX_W, Direction> Self; 

3. 注意将顶点放入到并查集中,防止边的重复。

测试用例

void TestGraphMinTree()
{
	const char* str = "abcdefghi";
	matrix::Graph<char, int> g(str, strlen(str));
	g.AddEdge('a', 'b', 4);
	g.AddEdge('a', 'h', 8);
	//g.AddEdge('a', 'h', 9);
	g.AddEdge('b', 'c', 8);
	g.AddEdge('b', 'h', 11);
	g.AddEdge('c', 'i', 2);
	g.AddEdge('c', 'f', 4);
	g.AddEdge('c', 'd', 7);
	g.AddEdge('d', 'f', 14);
	g.AddEdge('d', 'e', 9);
	g.AddEdge('e', 'f', 10);
	g.AddEdge('f', 'g', 2);
	g.AddEdge('g', 'h', 1);
	g.AddEdge('g', 'i', 6);
	g.AddEdge('h', 'i', 7);
	matrix::Graph<char, int> kminTree;
	cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
	kminTree.Print();
}

Prim算法

算法思路

Prim算法所具有的一个性质是集合A中的边总是构成一棵树。这棵树从一个任意的根节点r开始,一直扩大到图中的所有顶点为止。在每一步连接集合A和A之外的顶点的所有边中,选择一条权值最小的边加入到A中。当算法结束时,A中的边形成一棵最小生成树。

因为添加边只会在当前集合中没有的顶点中进行,所以Prim算法天然避免环的生成,不需要使用并查集来避免环的产生。

代码实现

实现思路:

1. 使用两个数组表示X、Y集合,用于表示当前顶点是否被访问。

2. 从X集合中的顶点选出所有的边,可以使用优先级队列来存放边。

3. 依次从优先级队列中选出权值最小的边

4. 判断该边是否成环---即dest顶点必须在Y集合中

5. 在Y集合中则将该边添加到最小生成树中,然后进行顶点的标记

6. 再将dest顶点连通的边再放入队列中,同时也要判断dest连接的顶点归属Y集合。

7. 如果选出n-1条边,返回生成树的总权值,否则返回默认值


W  Prim(Self& minTree, const W& src)   //src表示从哪个起点开始
{
	size_t srci = GetVertexIndex(src);
	size_t n = _vertexs.size();
	//初始化minTree
	minTree._vertexs = _vertexs;
	minTree._indexMap = _indexMap;
	minTree._matrix = vector<vector<W>>(n, vector<W>(n, MAX_W));

	//两个数组表示X、Y集合,用于表示当前顶点是否被访问
	vector<bool> X(n, false);
	vector<bool> Y(n, true);
	//初始化X,Y集合
	X[srci] = true;
	Y[srci] = false;
	//从X-Y集合连接中的边选出权值最小的边
	priority_queue<Edge, vector<Edge>, greater<Edge>> minque;
	//先把srci连接的边添加到队列中
	for (size_t i = 0; i < n; i++)
	{
		if (_matrix[srci][i] != MAX_W)
		{
			minque.push(Edge(srci, i, _matrix[srci][i]));
		}
	}
	size_t size = 0;
	W total_W = W();
	while (!minque.empty())
	{
		Edge min = minque.top();
		minque.pop();
		if (Y[min._dsti])  //该顶点必须还在Y集合中   注意!!
		{
			cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] <<
				":" << _matrix[min._srci][min._dsti] << endl;
			minTree._AddEdge(min._srci, min._dsti, min._w);
			total_W += min._w;
			size++;

			if (size == n - 1)
				break;

			X[min._dsti] = true;
			Y[min._dsti] = false;
			//将dsti的边进行遍历
			for (size_t i = 0; i < n; i++)
			{
				if (_matrix[min._dsti][i] != MAX_W && Y[i])    //有连通,并且是Y集合中的顶点
				{
					minque.push(Edge(min._dsti, i, _matrix[min._dsti][i]));
				}
			}
		}
	}
	//如果该图不是连通图,则没有最小生成树
	if (size < n - 1)
	{
		return W();
	}
	return total_W;
}

测试用例

void TestGraphMinTree()
{
	const char str[] = "abcdefghi";
	matrix::Graph<char, int> g(str, strlen(str));
	g.AddEdge('a', 'b', 4);
	g.AddEdge('a', 'h', 8);
	g.AddEdge('b', 'c', 8);
	g.AddEdge('b', 'h', 11);
	g.AddEdge('c', 'i', 2);
	g.AddEdge('c', 'f', 4);
	g.AddEdge('c', 'd', 7);
	g.AddEdge('d', 'f', 14);
	g.AddEdge('d', 'e', 9);
	g.AddEdge('e', 'f', 10);
	g.AddEdge('f', 'g', 2);
	g.AddEdge('g', 'h', 1);
	g.AddEdge('g', 'i', 6);
	g.AddEdge('h', 'i', 7);
	/*matrix::Graph<char, int> kminTree;
	cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
	kminTree.Print();*/
	cout << "prim算法的实现" << endl;
	matrix::Graph<char, int> pminTree;
	cout << "Prim:" << g.Prim(pminTree, 'a') << endl;
	pminTree.Print();
}

最后细心的你可以会发现,两种算法的结果权值都是37,但是其生成的最小生成树是不同的,这就好比一个正三角形,三个顶点所在的位置,无论连接哪条线,其都是最小生成树,所以不唯一。

C++最小生成树(kruskal算法,prim算法) 生成树是一种特殊的连通子图,它在保持图中所有顶点的同时,只包含了构成一棵树的边数,即n-1条边,其中n是图中的顶点数量。生成树是无环的,并且是连通的,这意味着通过生成树可以从任何一个顶点到达其他任何顶点,而不需要回溯或形成环路。权值最小的生成树称为最小生成树。 阅读详情

相关推荐

最小生成树,Prim,Kruskal算法主要思想,证明及C++实现

最小生成树: 任何只由G的边构成,并包含G的所有顶点的树称为G的生成树(G连通).加权无向图G的生成树的代价是该生成树的所有边的代码(权)的和.最小代价生成树是其所有生成树中代价最小的生成树。 实现最小生成树的算法常用的是Prim,Kruskal学校数据结构的书上讲解了这两大算法的思路及用C++实现,但关于其合理性的证明却略过去了,这里主要加上我自己的一些总结,证明一下,最后写个模版用。 P

/ 9479

C++刷题周记(五)——并查集/最小生成树(Prim/Kruskal)

距离dis设成∞ ,每次找到距离最小的点t 加入路径中 修改状态,并用该点距离进行松弛操作,更新所有点到起点距离 dis[i]=min(dis[i],w+g[t][i]);的距离dis设成∞ ,每次找到距离最小的点t,加入到连通集合中,并用该点距离进行松弛操作,更新所有点到集合距离 dis[i]=min(dis[i],g[t][i])的距离dis设成∞ ,每次找到距离最小的点,加入到连通集合中,并用该点距离进行松弛操作,更新所有点到集合距离 dis[i]=min(dis[i],g[t][i])

funkoctopus的博客 523

c++最小生成树两种方法:Prim算法 和 Kruskal算法

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

镭射瓶的博客 605

Kruskal算法(c++实现)

算法原理: 1.将边的权值从小到大排列; 2.构造辅助数组root[n](n为节点个数) 3.按照边的权值从小到大的顺序考察各条边; 4.关键:如何判别被考察边的两个顶点是否位于两个连通分量(用到辅助函数);先将辅助函数各项初始化为-1,如果root[i]为-1;则顶点就为该连通分量的“跟”,对于边(u,v),设v1,v2分别为两个顶点所在的连通分量的根节点;如果v1不等于v2,则u,v必...

wxf明的博客 1万+

掌握最小生成树:Prim与Kruskal算法C++实现

最小生成树是图论中的一个重要概念,在解决网络设计和优化问题时具有广泛的应用。所谓最小生成树,是指在一个加权无向图中找到一个子图,该子图包含图中所有顶点,并且边的权值之和最小。理解最小生成树的概念对于掌握后续章节中将要讨论的Prim算法和Kruskal算法至关重要。最小生成树的定义简单明了:对于一个具有N个顶点的连通加权无向图,其最小生成树是一棵包含所有N个顶点的树,且所有边的权重之和最小。最小生成树具有以下性质:最小生成树包含图中所有的顶点。最小生成树是一棵树,即不存在环。

weixin_35935514的博客 888

基于Union-Find的Kruskal算法C++实现

基于Union-Find数据结构实现Kruskal求最小生成树,代码设计及变量命名附详细注释。基于Union-Find数据结构实现Kruskal求最小生成树,代码设计及变量命名附详细注释。

Kruskal(克鲁斯卡尔)——图论(C++)

一、基本思想 此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 把图中的所有边按代价从小到大排序; 把图中的n个顶点看成独立的n棵树组成的森林; 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。 图片来自a2392008643 prim算法适合稠密图,kruskal算法适合简.

Annabel_CM的博客 1388

c++实现最小生成树Kruskal算法

c++实现最小生成树Kruskal算法,课程作业,供大家参考~~

最小生成树算法之Kruskal算法(c++)

kruskal的算法实现

smart_jackli的博客 1719

Kruskal算法 最小生成树

克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用(所选的边不能构成回路)的最小权植边。所以Kruskal算法的第一步是给所有的边按照从小到大的顺序排序。这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每一条边(u,v)。 具体实现过程如下: <1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。 <2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量 上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。 <3> 如此重复下去,直到所有顶点在同一连通分量上为止。

C++语言程序 最小代价生成树(kruskal算法)

C++语言程序 最小代价生成树(kruskal算法) 含代码解释

数据结构C++——最小生成树之Prim算法和Kruskal算法

数据结构C++——最小生成树之Prim算法和Kruskal算法 文章目录数据结构C++——最小生成树之Prim算法和Kruskal算法一、最小生成树的基本概念二、最小生成树之Prim算法①Prim算法的实现原理②Prim算法中的Min函数的实现③Prim算法的代码实现④测试的完整代码三、最小生成树之Kruskal算法①Kruskal算法的实现原理②Krusal算法的代码实现③测试的完整代码四、总结 一、最小生成树的基本概念 最小生成树: 在一个连通网的所有生成树中,各边的代价之和最小的那棵生成树称为该连通网

qq_54162207的博客 8319

C++使用Kruskal和Prim算法实现最小生成树

很久以前就学过最小生成树之Kruskal和Prim算法,这两个算法很容易理解,但实现起来并不那么容易。最近学习了并查集算法,得知并查集可以用于实现上述两个算法后,我自己动手实现了最小生成树算法。 宏观上讲,Kruskal算法就是一个合并的过程,而Prim算法是一个吞并的过程,另外在Prim算法中还用到了一种数据结构——优先级队列,用于动态排序。由于这两个算法很容易理解,在此不再赘述。接下来给出我的源代码。 输入 第一行包含两个整数n和m,n表示图中结点个数,m表示图中边的条数;接下来m行,每一行包含三个整数u,v,w,表示途中存在一条边(u,v),并且其权重为w;为了便于调试,我的程序是从文件

最小生成树之Kruskal和Prim算法——C++实现

很久以前就学过最小生成树之Kruskal和Prim算法,这两个算法很容易理解,但实现起来并不那么容易。最近学习了并查集算法,得知并查集可以用于实现上述两个算法后,我自己动手实现了最小生成树算法。 宏观上讲,Kruskal算法就是一个合并的过程,而Prim算法是一个吞并的过程,另外在Prim算法中还用到了一种数据结构——优先级队列,用于动态排序。由于这两个算法很容易理解,在此不再赘述。接下来给出我

疯狂的指针的博客 1万+

思科9117AP从零配置EWC模式实战:Wi-Fi6网络搭建避坑指南

本文详细介绍了思科9117AP在Wi-Fi6网络环境下配置EWC模式的实战指南,涵盖镜像准备、网络环境配置、模式转换步骤及高级运维技巧。通过EWC模式,企业可实现控制器与AP的一体化部署,显著降低成本和简化运维,特别适合中小企业Wi-Fi6网络搭建。

weixin_28318011的博客 332

图论算法:最小生成树——Prim算法和Kruskal算法C++实现

前言:        这次我记录的是另外一种很有意思的东西——最小生成树。相信学过离散数学的朋友都对这种东西不陌生,那么在代码中,我们该如何实现它呢?接下来,我将对此讲解一些自己的心得。        开头让我插入一个Github传送门:JZX555的Github原理:      首先让我们看看什么是最小生成树——一个有n个节点的连通图的生成树是原图的极小连通子图,且包含原图中的所有n个节点,并且...

weixin_41427400的博客 2820

告别安装报错!保姆级教程:用阿狸狗破戒大师V3.2.6搞定Cadence 17.4完整安装与补丁

本文提供Cadence 17.4的完整安装与破解教程,特别针对国内用户常见问题优化。从前期准备、镜像处理到阿狸狗破戒大师V3.2.6的智能破解,再到补丁管理与性能调优,详细指导每一步操作,确保一次性安装成功并提升设计效率。

weixin_30632089的博客 832

最小生成树Prim与Kruskal 算法【C++实现】

1584. 连接所有点的最小费用 - 力扣(LeetCode)prim算法核心思路在于初始化边矩阵,设置一个最小距离数组和是否在树中的数组。

keith66的博客 603

别再手动写JSON了!用Node-RED OPC UA节点5分钟搞定楼宇温湿度数据采集

本文介绍了如何利用Node-RED和OPC UA节点快速实现楼宇温湿度数据的自动化采集与JSON格式转换。通过可视化工具Node-RED,开发者无需编写复杂代码,5分钟内即可完成从传感器到数据输出的全流程配置,大幅提升智慧楼宇和工业物联网项目的实施效率。

weixin_30368405的博客 431

拌合楼管理软件开发(十一) 海康威视车牌识别摄像头安装调试,总算是跑通了。

/保存的文件个数这里之所以需要用到委托,直接更新textbox文本中的内容,以及复制picturebox的image会提示不是从齐创建的线程调用。

morsth的博客 4814

【贪心】最小生成树问题 Prim算法 + Kruskal算法(C++ 附代码及实例)

设G=(V,E)是一个无向连同带权图,即一个网络E中每条边(v,w)的权为c[v][w]如果G的子图G是一棵包含G1的所有顶点的树,则称G1为G的生成树生成树各边权的总和称为该生成树的在G的所有生成树中,耗费最小的生成树称为G的,Minimum Spanning Tree,简称MST。

lhr056813的博客 998

Vue3 - vite 项目报错:Uncaught ReferenceError: require is not defined(解决 vue3 vite 项目无法识别 require 语法导致报错)

vue3,vite,报错解决,vue3+vite报错requireisnotdefined,vue3vite引插件不识别require,老插件必须用require怎么办,vite项目插件要求使用require引入但报错,vue3不兼容require导致第三方包插件无法使用,vue3vite中使用require报错怎么办,解决Vite+vue3使用第三方依赖报错“requireisnotdefined”问题,vite项目require语法兼容问题解决requireisnotdefine

🏆 前端领域优质创作者 2万+

C++求最小生成树Prim和Kruskal算法

代码如下带有注释:欢迎交流#include&lt;iostream&gt;#include&lt;cstdio&gt;#include&lt;cstring&gt;#include&lt;algorithm&gt;using namespace std;int select(int shortedge[],int n){    int k,i,min1=9999;    for(i=1;i&lt;...

qq_40458569的博客 879

ADES1831和ADBMS6830

1、上电默认进入STANDBY模式,SPI发送0xFF也可以进入STANDBY模式2、看门狗定时器超时后,进入SLEEP状态3、配置寄存器中的REFON位使用WRCFGA命令将组A设置为1进入REFUP状态,此状态比待机状态更快地启动ADC转换。i<TOTAL_IC;i++)注意:读AFE数据时会随着AFE片数增加而增加,原因是唤醒时间和AFE片数密切相关,如果需要缩短时间,那么减少唤醒频次。

m0_61973119的博客 263

Python赋能,洞见先机:构建基于异步爬虫与流处理的实时舆情监控系统

我们将从核心概念入手,逐步搭建一个集。.option("es.resource", "public-opinion-%s" % time.strftime("%Y-%m-%d")) \ # 按日期分索引。| 数据采集层 |----->| 消息队列层 |----->| 流处理与分析层 |----->| 存储与可视化层 |:使用Spark Streaming的状态操作或Flink的CEP库,检测更复杂的模式,如“某关键词的负面舆情在1小时内激增500%”。

2201_76125261的博客 433
上一篇: 图的深度优先与广度优先遍历
Brant_zero2022
博客等级 码龄5年 582粉丝 108原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Brant_zero2022

素材免费分享不求打赏,只求关注

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

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

打赏作者

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

抵扣说明:

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

余额充值