最小生成树(Prim算法与Kruskal算法)

这篇具有很好参考价值的文章主要介绍了最小生成树(Prim算法与Kruskal算法)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么是最小生成树

一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树。

例如下图中①、②、③都是左侧图的生成树,但③是构造连通网的最小代价,所以③是该图的最小生成树。
最小生成树(Prim算法与Kruskal算法)

二、Prim算法

Prim算法的核心思想如下:

  • ① 将图中所有顶点分为A、B两类,初始时所有顶点都在B类
  • ② 选择任意一个顶点,将其放入A类
  • ③ 从B类所有顶点出发,找一条连接A类某个顶点且权值最小的边。将这条边连接的B类中的顶点放入A类中。
  • ④ 重复③步骤,直到所有B类中的顶点全部放入A类。

下面通过图来说明最小生成树的构建过程

首先,在初始时,所有顶点都在B类
最小生成树(Prim算法与Kruskal算法)

选择顶点A放入A类中,然后寻找B类到A类权值最小的边。很显然是BA这条边
最小生成树(Prim算法与Kruskal算法)

将顶点B放入A类中,然后继续寻找B类到A类权值最小的边。结果是AE
最小生成树(Prim算法与Kruskal算法)

将顶点E放入A类中,继续寻找。结果是AC
最小生成树(Prim算法与Kruskal算法)

将顶点C放入A类中,继续寻找。结果只有BD
最小生成树(Prim算法与Kruskal算法)

将顶点D加入A类,构建结束。

原理很简单,直接上代码。这里的图采用邻接矩阵存储

// 边结构
struct Edge:IComparable<Edge>
{
    public int from;
    public int to;
    public int weight;
    
    public int CompareTo(Edge other)
    {
        return weight - other.weight;
    }
}

private void Prim<T>(GraphByAdjacencyMatrix<T> graph)
{
	var graphCount = graph.Count;
	// 用来记录遍历过的顶点
	bool[] nodes = new bool[graphCount];
	// 用来记录当前遍历到的边
	Edge[] edges = new Edge[graphCount];

	// 将第一个顶点设置为已遍历
	nodes[0] = true;
	// 将第一个顶点对应的边加入集合
	// 都从1开始遍历是因为n个顶点对应n-1条边
	for (int i = 1; i < graphCount; i++)
	{
		edges[i] = new Edge {from = 0, to = i, weight = graph.Matrix[0, i]};
	}

	for (int i = 1; i < graphCount; i++)
	{
		// 找出权值最小的边
		int min = Int32.MaxValue;
		int minIndex = 0;
		for (int j = 1; j < graphCount; j++)
		{
			if (!nodes[j] && edges[j].weight < min)
			{
				min = edges[j].weight;
				minIndex = j;
			}
		}

		// 将新的顶点加入已遍历集合
		nodes[minIndex] = true;
		// 打印边
		Console.Write($"({edges[minIndex].from},{edges[minIndex].to}) ");

		// 将新的顶点对应的边加入集合
		// 忽略已经访问过的顶点、忽略比当前遍历的边更长的边
		for (int j = 1; j < graphCount; j++)
		{
			if (!nodes[j] && edges[j].weight > graph.Matrix[minIndex, j])
			{
				edges[j] = new Edge {from = minIndex, to = j, weight = graph.Matrix[minIndex, j]};
			}
		}
	}
}

Prim算法关注的是顶点,通过寻找各顶点上权值最小的边,逐步构建起最小生成树。Prim算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),n为顶点数。因此对于边数非常多的稠密图,Prim算法在性能上会更有优势。

三、Kruskal算法

与Prim算法关注顶点的思路不同,Kruskal算法关注点在于边。它的原理也很简单,就是先将所有的边按权值从小到大进行排序。然后遍历边集,只要遍历到的这条边不会与结果集中的边形成环,就将其加入结果集。
最小生成树(Prim算法与Kruskal算法)

代码如下

private void Kruskal<T>(GraphByAdjacencyMatrix<T> graph)
{
	// 自己实现的小根堆,用来对边排序
	HeapList<Edge> edges = new HeapList<Edge>();
	// 一维数组用来检验是否成环
	int[] parent = new int[graph.Count];
	
	// 将边加入小根堆
	for (int i = 0; i < graph.Count; i++)
	{
		for (int j = i+1; j < graph.Count; j++)
		{
			if(graph.Matrix[i,j] == Int32.MaxValue) continue;
			edges.Push(new Edge(){from = i,to=j,weight = graph.Matrix[i,j]});
		}
	}

	for (int i = 0; i < graph.Count; i++)
	{
		// 弹出权值最小的边
		var edge = edges.Pop();
		int m = Find(parent, edge.from);
		int n = Find(parent, edge.to);
		
		// 如果n!=m,则未形成环路
		if (n != m)
		{
			parent[m] = n;
			// 打印边
			Console.Write($"({edge.from},{edge.to})");
		}
	}
}

/// <summary>
/// 校验是否成环
/// </summary>
private int Find(int[] parent,int index)
{
	while (parent[index] != 0)
	{
		index = parent[index];
	}
	return index;
}

这里的parent[]的作用可能有些难以理解。事实上它相当于一个并查集,用来检验是否成环。我们通过前面的例子来具体说明
最小生成树(Prim算法与Kruskal算法)

首先进行校验的边是 ( 1 , 2 ) (1,2) (1,2),此时parent[]中的元素都为0,因此返回的m = 1,n = 2,因为m != n,所以将parent[1] = 2。这步操作意味着将顶点B(下标为1)和C(下标为2)加入了集合,且集合的代表为C
最小生成树(Prim算法与Kruskal算法)

接下来进行校验的边是 ( 0 , 1 ) (0,1) (0,1)。返回的m = 0,n = 2,所以将parent[0] = 2。即顶点A(下标为0)加入C这个集合
最小生成树(Prim算法与Kruskal算法)

下一条边为 ( 0 , 4 ) (0,4) (0,4),返回的m = 2,n = 4,所以将parent[2] = 4。即将C集合整个加入E所在的集合
最小生成树(Prim算法与Kruskal算法)

接下来是 ( 0 , 2 ) (0,2) (0,2),返回的m = 4,n = 4。此时n == m,意味着两个节点所在的集合都为E集合。也就是说这两个节点本身就是连通的,所以添加这条新的边会使生成树成环,需要舍弃。

接下来是 ( 1 , 3 ) (1,3) (1,3),返回的m = 4,n = 3,所以将parent[4] = 3,即将E集合加入D所在的集合
最小生成树(Prim算法与Kruskal算法)

生成树构建完成,退出循环。

当图的边数为 e e e时,Find()函数的时间复杂度为 l o g e loge loge,外层循环的时间复杂度为 e e e。因此整个算法的时间复杂度为 e l o g e eloge eloge。Kruskal算法对于边数较少的稀疏图在性能上有很大优势。

四、参考资料

[1].《大话数据结构》
[2]. http://c.biancheng.net/algorithm/prim.html文章来源地址https://www.toymoban.com/news/detail-451056.html

到了这里,关于最小生成树(Prim算法与Kruskal算法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

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

    连通图: 在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1和顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。 生成树: 一个连通图的最小连通子图称作为图的生成树。有 n个顶点 的连通图的生成树有 n个顶点和 n-1 条边。 最小生成树: 最小生活

    2024年02月10日
    浏览(32)
  • 【最小生成树】一文学懂prim、kruskal算法

    博主简介: 努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:算法、数据结构、Java等相关知识。 博主主页: @是瑶瑶子啦 所属专栏: 算法 ;该专栏专注于蓝桥杯和ACM等算法竞赛🔥 近期目标: 写好专栏的每一篇文章 首先,我们要了解什么是最小生

    2023年04月25日
    浏览(25)
  • 用prim和kruskal算法求最小生成树问题

    题目 http://ybt.ssoier.cn:8088/problem_show.php?pid=1350 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn) http://ybt.ssoier.cn:8088/problem_show.php?pid=1391 相当于一个图中求最小生成树的问题 prim解决 kruskal解法 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn) http://ybt.ssoier.cn:8088/problem_show.ph

    2024年02月09日
    浏览(29)
  • C语言实现最小生成树算法:Prim和Kruskal

    以下是使用C语言实现Prim算法生成最小生成树的代码: 注释如下: #include stdio.h 和 `#include #define V 5 定义了图中顶点的个数为5。 int minDistance(int dist[], int visited[]) 函数用于找到顶点集合中未访问的顶点中距离最小的顶点。输入参数 dist 用于存储顶点到最小生成树的距离,输入

    2024年02月03日
    浏览(35)
  • 【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)

    最小生成树 有关树的定义 生成子图 :生成子图是从原图中选取部分节点以及这些节点之间的边所组成的图。生成子图中的所有节点和边都必须在原图中存在。 生成树 :一个连通 无向图 的 生成子图 ,同时要求是树。也即在图的边集中选择 n - 1 条,将所有顶点连通。 我们

    2024年02月16日
    浏览(35)
  • 求最小生成树Prim(普里姆)和Kruskal(克鲁斯卡尔)算法

     想求最小生成树,我们首先得弄懂以下几个概念   连通图 :图中任意两个顶点都是连通的 极小连通子图 :既要保持图连通又要使得边数最少的子图 生成树 : 包含图中全部顶点的一个极小连通子图 连通图用通俗的话来讲就是,某一个顶点,可以 直接或者间接 (通过其他顶点

    2024年02月05日
    浏览(38)
  • 【数据结构与算法】最小生成树之普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法

      🌱博客主页:大寄一场. 🌱系列专栏:数据结构与算法 😘博客制作不易欢迎各位👍点赞+⭐收藏+➕关注 目录 前言 一、最小生成树的概念 二、最小生成树的求解方法 三、练习题 四、最小生成树在实际应用中的例子 最近非科班的同学学到了最小生成树并询问我,于是想趁

    2024年02月07日
    浏览(37)
  • 【算法导论】图论(图的基本概念,图上的深度优先搜索(DFS),广度优先搜索(BFS),最小生成树(MST)及Prim,Kruskal算法)

    图(Graph)是一种包含节点与节点的边的集合,记作G=(V,E),V是节点的集合,E是边的集合。 有向图 一个有向图G=(V,E),E中每个元素是V上的一个二值关系:一条从a出发的连向b的边e可以记作一个 有序 对e = (a,b) 。 无向图 一个无向图G=(V,E),E的每个元素e可以表示V上的一个 无序 对,记

    2024年02月03日
    浏览(40)
  • 【刷题】图论——最小生成树:Prim、Kruskal【模板】

    假设有n个点m条边。 Prim适用于邻接矩阵存的稠密图,时间复杂度是 O ( n 2 ) O(n^2) O ( n 2 ) ,可用堆优化成 O ( n l o g n ) O(nlogn) O ( n l o g n ) 。 Kruskal适用于稀疏图,n个点m条边,时间复杂度是 m l o g ( m ) mlog(m) m l o g ( m ) 。 Prim:遍历n次,每次选择 连通块和外面的点 到连通块距离

    2024年04月13日
    浏览(36)
  • 【蓝桥杯--图论】最小生成树prim、kruskal

    今日语录: 成功不是终点,失败不是致命,勇气才是取胜的关键。

    2024年01月24日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包