65.9K
CodeProject 正在变化。 阅读更多。
Home

Kruskal 算法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (43投票s)

2011年3月1日

CPOL

2分钟阅读

viewsIcon

214341

downloadIcon

11630

在 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
                }
            }
        } 

结论

希望我能提供一个清晰的解释。

© . All rights reserved.