Kruskal 最小生成树算法
对于一个给定的连通的无向图 G = (V, E),希望找到一个无回路的子集 T,T 是 E 的子集,它连接了所有的顶点,且其权值之和为最小。

因为 T 无回路且连接所有的顶点,所以它必然是一棵树,称为生成树(Spanning Tree),因为它生成了图 G。显然,由于树 T 连接了所有的顶点,所以树 T 有 V - 1 条边。一张图 G 可以有很多棵生成树,而把确定权值最小的树 T 的问题称为最小生成树问题(Minimum Spanning Tree)。术语 "最小生成树" 实际上是 "最小权值生成树" 的缩写。
Kruskal 算法提供一种在 O(ElogV) 运行时间确定最小生成树的方案。Kruskal 算法基于贪心算法(Greedy Algorithm)的思想进行设计,其选择的贪心策略就是,每次都选择权重最小的但未形成环路的边加入到生成树中。其算法结构如下:
- 将所有的边按照权重非递减排序;
- 选择最小权重的边,判断是否其在当前的生成树中形成了一个环路。如果环路没有形成,则将该边加入树中,否则放弃。
- 重复步骤 2,直到有 V - 1 条边在生成树中。
上述步骤 2 中使用了 Union-Find 算法来判断是否存在环路。
例如,下面是一个无向连通图 G。

图 G 中包含 9 个顶点和 14 条边,所以期待的最小生成树应包含 (9 - 1) = 8 条边。
首先对所有的边按照权重的非递减顺序排序:
Weight Src Dest
1 7 6
2 8 2
2 6 5
4 0 1
4 2 5
6 8 6
7 2 3
7 7 8
8 0 7
8 1 2
9 3 4
10 5 4
11 1 7
14 3 5
然后从排序后的列表中选择权重最小的边。
1. 选择边 {7, 6},无环路形成,包含在生成树中。

2. 选择边 {8, 2},无环路形成,包含在生成树中。

3. 选择边 {6, 5},无环路形成,包含在生成树中。

4. 选择边 {0, 1},无环路形成,包含在生成树中。

5. 选择边 {2, 5},无环路形成,包含在生成树中。

6. 选择边 {8, 6},有环路形成,放弃。
7. 选择边 {2, 3},无环路形成,包含在生成树中。

8. 选择边 {7, 8},有环路形成,放弃。
9. 选择边 {0, 7},无环路形成,包含在生成树中。

10. 选择边 {1, 2},有环路形成,放弃。
11. 选择边 {3, 4},无环路形成,包含在生成树中。

12. 由于当前生成树中已经包含 V - 1 条边,算法结束。
C# 实现的 Kruskal 算法如下。
using System;
using System.Collections.Generic;
using System.Linq; namespace GraphAlgorithmTesting
{
class Program
{
static void Main(string[] args)
{
Graph g = new Graph();
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , ); Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
Console.WriteLine(); Console.WriteLine("Is there cycle in graph: {0}", g.HasCycle());
Console.WriteLine(); Edge[] mst = g.Kruskal();
Console.WriteLine("MST Edges:");
foreach (var edge in mst)
{
Console.WriteLine("\t{0}", edge);
} Console.ReadKey();
} class Edge
{
public Edge(int begin, int end, int weight)
{
this.Begin = begin;
this.End = end;
this.Weight = weight;
} public int Begin { get; private set; }
public int End { get; private set; }
public int Weight { get; private set; } public override string ToString()
{
return string.Format(
"Begin[{0}], End[{1}], Weight[{2}]",
Begin, End, Weight);
}
} class Subset
{
public int Parent { get; set; }
public int Rank { get; set; }
} class Graph
{
private Dictionary<int, List<Edge>> _adjacentEdges
= new Dictionary<int, List<Edge>>(); public Graph(int vertexCount)
{
this.VertexCount = vertexCount;
} public int VertexCount { get; private set; } public IEnumerable<int> Vertices { get { return _adjacentEdges.Keys; } } public IEnumerable<Edge> Edges
{
get { return _adjacentEdges.Values.SelectMany(e => e); }
} public int EdgeCount { get { return this.Edges.Count(); } } public void AddEdge(int begin, int end, int weight)
{
if (!_adjacentEdges.ContainsKey(begin))
{
var edges = new List<Edge>();
_adjacentEdges.Add(begin, edges);
} _adjacentEdges[begin].Add(new Edge(begin, end, weight));
} private int Find(Subset[] subsets, int i)
{
// find root and make root as parent of i (path compression)
if (subsets[i].Parent != i)
subsets[i].Parent = Find(subsets, subsets[i].Parent); return subsets[i].Parent;
} private void Union(Subset[] subsets, int x, int y)
{
int xroot = Find(subsets, x);
int yroot = Find(subsets, y); // Attach smaller rank tree under root of high rank tree
// (Union by Rank)
if (subsets[xroot].Rank < subsets[yroot].Rank)
subsets[xroot].Parent = yroot;
else if (subsets[xroot].Rank > subsets[yroot].Rank)
subsets[yroot].Parent = xroot; // If ranks are same, then make one as root and increment
// its rank by one
else
{
subsets[yroot].Parent = xroot;
subsets[xroot].Rank++;
}
} public bool HasCycle()
{
Subset[] subsets = new Subset[VertexCount];
for (int i = ; i < subsets.Length; i++)
{
subsets[i] = new Subset();
subsets[i].Parent = i;
subsets[i].Rank = ;
} // Iterate through all edges of graph, find subset of both
// vertices of every edge, if both subsets are same,
// then there is cycle in graph.
foreach (var edge in this.Edges)
{
int x = Find(subsets, edge.Begin);
int y = Find(subsets, edge.End); if (x == y)
{
return true;
} Union(subsets, x, y);
} return false;
} public Edge[] Kruskal()
{
// This will store the resultant MST
Edge[] mst = new Edge[VertexCount - ]; // Step 1: Sort all the edges in non-decreasing order of their weight
// If we are not allowed to change the given graph, we can create a copy of
// array of edges
var sortedEdges = this.Edges.OrderBy(t => t.Weight);
var enumerator = sortedEdges.GetEnumerator(); // Allocate memory for creating V ssubsets
// Create V subsets with single elements
Subset[] subsets = new Subset[VertexCount];
for (int i = ; i < subsets.Length; i++)
{
subsets[i] = new Subset();
subsets[i].Parent = i;
subsets[i].Rank = ;
} // Number of edges to be taken is equal to V-1
int e = ;
while (e < VertexCount - )
{
// Step 2: Pick the smallest edge. And increment the index
// for next iteration
Edge nextEdge;
if (enumerator.MoveNext())
{
nextEdge = enumerator.Current; int x = Find(subsets, nextEdge.Begin);
int y = Find(subsets, nextEdge.End); // If including this edge does't cause cycle, include it
// in result and increment the index of result for next edge
if (x != y)
{
mst[e++] = nextEdge;
Union(subsets, x, y);
}
else
{
// Else discard the nextEdge
}
}
} return mst;
}
}
}
}
输出结果如下:

参考资料
- Connectivity in a directed graph
- Strongly Connected Components
- Tarjan's Algorithm to find Strongly Connected Components
本篇文章《Kruskal 最小生成树算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。
Kruskal 最小生成树算法的更多相关文章
- [算法系列之二十七]Kruskal最小生成树算法
简单介绍 求最小生成树一共同拥有两种算法,一个是就是本文所说的Kruskal算法,还有一个就是Prime算法. 在具体解说Kruskal最小生成树算法之前,让我们先回想一下什么是最小生成树. 我们有一 ...
- 并查集和kruskal最小生成树算法
并查集 先定义 int f[10100];//定义祖先 之后初始化 for(int i=1;i<=n;++i) f[i]=i; //初始化 下面为并查集操作 int find(int x)//i ...
- [算法] kruskal最小生成树算法
#include <stdio.h> #include <stdlib.h> #define MAX 100 int N, M; struct Edge { int u,v; ...
- 贪心算法(2)-Kruskal最小生成树
什么是最小生成树? 生成树是相对图来说的,一个图的生成树是一个树并把图的所有顶点连接在一起.一个图可以有许多不同的生成树.一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n ...
- 最小生成树算法(Prim,Kruskal)
边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...
- c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树
c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路 ...
- [数据结构]最小生成树算法Prim和Kruskal算法
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总 ...
- 无向带权图的最小生成树算法——Prim及Kruskal算法思路
边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...
- 最小生成树之克鲁斯卡尔(Kruskal)算法
学习最小生成树算法之前我们先来了解下 下面这些概念: 树(Tree):如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的 ...
随机推荐
- Python 实现隐藏文件夹、文件操作
Python通过win32api 可以实现操作文件夹文件操作,获取属性,修改属性 1.获取属性 通过win32api.GetFileAttributes 方法可以获取属性值 import win32c ...
- linux shell基础命令
du -h #查询磁盘文件大小和列表 df -h # 查询服务器磁盘使用情况 top/free # 查询服务器内存,cpu等资源使用情况 iptables # 防火墙相关的命令 vi ...
- JS(去掉前后空格或去掉所有空格)的用法
1. 去掉字符串前后所有空格: 代码如下: function Trim(str) { return str.replace(/(^\s*)|(\s*$)/g, ""); } 说明 ...
- VOF 方法捕捉界面--粘性剪切流动算例
流体体积法(Volume ofFluid)是一种典型的界面追踪方法,这种方法选取流体体积分数为界面函数S.它通过定义一个体积分数$ C $(指定的流体体积分数占网格体积的百分比)来描述界面.因此只有所 ...
- PDA固定资产条码管理系统软件-解决固定资产实物清查的瓶颈问题,大大提高清查效率
固定资产管理系统是企业信息化管理中的一个重要组成部分,固定资产具有价值高,使用周期长.使用地点分散.管理难度大等特点.一个企业的良性发展,避免不了的要涉及到企业资产的有效管理.对于那些技术装备密集型的 ...
- Android进阶系列之源码分析Activity的启动流程
美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...
- Simulink Memory vs Unit Delay
Memoryブロック.Unit Delayブロック共に前回の入力値を出力しますが.動作するタイミングが異なります. ●Memoryブロック シミュレーションの各時刻(ステップ)で動作し.「1ステップ」 ...
- div高度根据内容自动增大
1.很多时候我们希望容器高度能够自适应内部元素的变化,需要用到min-height属性. 2.有时候用了min-height还是不会随着内容自适应高度,您需要检查下容器的子元素是不是有浮动属性,当子元 ...
- haahah
#DB ``` import os basedir = os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI = ' ...
- e.preventDefault() e.stopPropagation()和return false的区别
e.preventDefault(); //阻止事件的默认行为,比如a标签的转向,但不阻止事件的冒泡传播e.stopPropagation() //阻止事件的冒泡传播,但不阻止其默认行为returne ...