Kruskal 算法






4.84/5 (43投票s)
在 C# 中实现 Kruskal 算法

引言
根据 维基百科:“Kruskal 算法是 算法 在 图论 中的一种,用于寻找一个 最小生成树 给一个 连通加权图。这意味着它找到一个 边 的子集,形成一棵包含每个 顶点 的树,其中树中所有边的总权重最小化。如果图不是连通的,那么它会找到一个最小生成森林(每个 连通分量 的最小生成树)。Kruskal 算法是 贪心算法 的一个例子。”
简而言之,Kruskal 算法用于以尽可能低的成本连接图中的所有节点。
示例
一家有线电视公司正在在新社区铺设电缆。一家网吧正在通过网络连接所有电脑。
使用演示
点击任意位置绘制顶点。按住 ctrl 并选择两个顶点以创建一条 边。会弹出一个窗口来输入边的成本。完成绘制图后,点击 求解。
高层设计
低层设计
Using the Code
IList<Edge> Solve(IList<Edge> graph, out int totalCost);
如何使用 GDI 形成图不在此讨论范围内,因为它与主题无关。
类
通常,我们的图由两个组件组成,即 顶点 和连接这些顶点的 边。
每个 边 标记有一个值或权重,即连接两个顶点的 成本。
顶点
持有
- 顶点 名称(必须在图中是唯一的)及其 绘制点
- 秩 和 根(稍后会讲到)
using System;
using System.Drawing;
namespace Kruskal
{
public class Vertex
{
#region Members
private int name;
#endregion
#region Public Properties
public int Name
{
get
{
return name;
}
}
public int Rank { get; set; }
public Vertex Root { get; set; }
public Point Position { get; set; }
#endregion
#region Constructor
public Vertex(int name, Point position)
{
this.name = name;
this.Rank = 0;
this.Root = this;
this.Position = position;
}
#endregion
#region Methods
internal Vertex GetRoot()
{
if (this.Root != this)// am I my own parent ? (am i the root ?)
{
this.Root = this.Root.GetRoot();// No? then get my parent
}
return this.Root;
}
internal static void Join(Vertex root1, Vertex root2)
{
if (root2.Rank < root1.Rank)//is the rank of Root2 less than that of Root1 ?
{
root2.Root = root1;//yes! then Root1 is the parent of Root2 (since it has the higher rank)
}
else //rank of Root2 is greater than or equal to that of Root1
{
root1.Root = root2;//make Root2 the parent
if (root1.Rank == root2.Rank)//both ranks are equal ?
{
root2.Rank++;//increment Root2, we need to reach a single root for the whole tree
}
}
}
#endregion
}
}
Edge
持有两个 顶点、它们之间的连接 成本 以及 成本绘制点。
请注意,它实现了 IComparable
,稍后我们需要它来按 成本 对 边 进行排序。
using System;
using System.Drawing;
namespace Kruskal
{
public class Edge : IComparable
{
#region Members
private Vertex v1, v2;
private int cost;
private Point stringPosition;
#endregion
#region Public Properties
public Vertex V1
{
get
{
return v1;
}
}
public Vertex V2
{
get
{
return v2;
}
}
public int Cost
{
get
{
return cost;
}
}
public Point StringPosition
{
get
{
return stringPosition;
}
}
#endregion
#region Constructor
public Edge(Vertex v1, Vertex v2, int cost, Point stringPosition)
{
this.v1 = v1;
this.v2 = v2;
this.cost = cost;
this.stringPosition = stringPosition;
}
#endregion
#region IComparable Members
public int CompareTo(object obj)
{
Edge e = (Edge)obj;
return this.cost.CompareTo(e.cost);
}
#endregion
}
}
算法实现
排序边
使用 快速排序,根据 成本 按升序对 边 进行排序。
创建集合
最初,每个 顶点 都是它自己的根,并且秩为零。
public Vertex(int name, Point position)
{
this.name = name;
this.Rank = 0;
this.Root = this;
this.Position = position;
}
为什么要这样做?
我们需要它来确保在添加我们的 顶点 时,我们不会形成一个 环路。
考虑这个例子
边 BD 没有被考虑,因为 B,D 已经通过 B,A,D 连接。
因此,对于我们检查的每个 边,我们必须检查它的两个 顶点 属于 不同的集合(树)。
如何确定两个顶点是否在不同的集合中?
使用递归函数 GetRoot()
。
internal Vertex GetRoot()
{
if (this.Root != this)// am I my own parent ? (am i the root ?)
{
this.Root = this.Root.GetRoot();// No? then get my parent
}
return this.Root;
}
如果根确实不同(每个 顶点 都在不同的集合中),则 Join()
这两个 顶点。
internal static void Join(Vertex root1, Vertex root2)
{
if (root2.Rank < root1.Rank)//is the rank of Root2 less than that of Root1 ?
{
root2.Root = root1;//yes! then Root1 is the parent of Root2 (since it has the higher rank)
}
else //rank of Root2 is greater than or equal to that of Root1
{
root1.Root = root2;//make Root2 the parent
if (root1.Rank == root2.Rank)//both ranks are equal ?
{
root2.Rank++;//increment Root2, we need to reach a single root for the whole tree
}
}
}
结论
希望我能提供一个清晰的解释。