来古计算机 > 软件编程 > 正文

计算机经典算法之Kruskal算法

 经典算法之Kruskal算法

一:思想

    若存在M={0,1,2,3,4,5}这样6个节点,我们知道Prim算法构建生成树是从”顶点”这个角度来思考的,然后采用“贪心思想”

来一步步扩大化,最后形成整体最优解,而Kruskal算法有点意思,它是站在”边“这个角度在思考的,首先我有两个集合。

1. 顶点集合(vertexs):

     比如M集合中的每个元素都可以认为是一个独根树(是不是想到了并查集?)。

2.边集合(edges):

    对图中的每条边按照权值大小进行排序。(是不是想到了优先队列?)

好了,下面该如何操作呢?

首先:我们从edges中选出权值最小的一条边来作为生成树的一条边,然后将该边的两个顶点合并为一个新的树。

然后:我们继续从edges中选出次小的边作为生成树的第二条边,但是前提就是边的两个顶点一定是属于两个集合中,如果不是

        则剔除该边继续选下一条次小边。

最后:经过反复操作,当我们发现n个顶点的图中生成树已经有n-1边的时候,此时生成树构建完毕。

从图中我们还是很清楚的看到Kruskal算法构建生成树的详细过程,同时我们也看到了”并查集“和“优先队列“这两个神器

来加速我们的生成树构建。

 

二:构建

1.Build方法

这里我灌的是一些测试数据,同时在矩阵构建完毕后,将顶点信息放入并查集,同时将边的信息放入优先队列,方便我们在

做生成树的时候秒杀。

 

 1 #region 矩阵的构建
 2         /// <summary>
 3         /// 矩阵的构建
 4         /// </summary>
 5         public void Build()
 6         {
 7             //顶点数
 8             graph.vertexsNum = 6;
 9 
10             //边数
11             graph.edgesNum = 8;
12 
13             graph.vertexs = new int[graph.vertexsNum];
14 
15             graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
16 
17             //构建二维数组
18             for (int i = 0; i < graph.vertexsNum; i++)
19             {
20                 //顶点
21                 graph.vertexs[i] = i;
22 
23                 for (int j = 0; j < graph.vertexsNum; j++)
24                 {
25                     graph.edges[i, j] = int.MaxValue;
26                 }
27             }
28 
29             graph.edges[0, 1] = graph.edges[1, 0] = 80;
30             graph.edges[0, 3] = graph.edges[3, 0] = 100;
31             graph.edges[0, 5] = graph.edges[5, 0] = 20;
32             graph.edges[1, 2] = graph.edges[2, 1] = 90;
33             graph.edges[2, 5] = graph.edges[5, 2] = 70;
34             graph.edges[4, 5] = graph.edges[5, 4] = 40;
35             graph.edges[3, 4] = graph.edges[4, 3] = 60;
36             graph.edges[2, 3] = graph.edges[3, 2] = 10;
37 
38             //优先队列,存放树中的边
39             queue = new PriorityQueue<Edge>();
40 
41             //并查集
42             set = new DisjointSet<int>(graph.vertexs);
43 
44             //将对角线读入到优先队列
45             for (int i = 0; i < graph.vertexsNum; i++)
46             {
47                 for (int j = i; j < graph.vertexsNum; j++)
48                 {
49                     //说明该边有权重
50                     if (graph.edges[i, j] != int.MaxValue)
51                     {
52                         queue.Eequeue(new Edge()
53                         {
54                             startEdge = i,
55                             endEdge = j,
56                             weight = graph.edges[i, j]
57                         }, graph.edges[i, j]);
58                     }
59                 }
60             }
61         }
62         #endregion

2:Kruskal算法

并查集,优先队列都有数据了,下面我们只要出队操作就行了,如果边的顶点不在一个集合中,我们将其收集作为最小生成树的一条边,

按着这样的方式,最终生成树构建完毕,怎么样,组合拳打的爽不爽?

1 #region Kruskal算法
 2         /// <summary>
 3         /// Kruskal算法
 4         /// </summary>
 5         public List<Edge> Kruskal()
 6         {
 7             //最后收集到的最小生成树的边
 8             List<Edge> list = new List<Edge>();
 9 
10             //循环队列
11             while (queue.Count() > 0)
12             {
13                 var edge = queue.Dequeue();
14 
15                 //如果该两点是同一个集合,则剔除该集合
16                 if (set.IsSameSet(edge.t.startEdge, edge.t.endEdge))
17                     continue;
18 
19                 list.Add(edge.t);
20 
21                 //然后将startEdge 和 endEdge Union起来,表示一个集合
22                 set.Union(edge.t.startEdge, edge.t.endEdge);
23 
24                 //如果n个节点有n-1边的时候,此时生成树已经构建完毕,提前退出
25                 if (list.Count == graph.vertexsNum - 1)
26                     break;
27             }
28 
29             return list;
30         }
31         #endregion

最后是总的代码:


 1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Diagnostics;
  6 using System.Threading;
  7 using System.IO;
  8 using System.Threading.Tasks;
  9 
 10 namespace ConsoleApplication2
 11 {
 12     public class Program
 13     {
 14         public static void Main()
 15         {
 16             MatrixGraph graph = new MatrixGraph();
 17 
 18             graph.Build();
 19 
 20             var edges = graph.Kruskal();
 21 
 22             foreach (var edge in edges)
 23             {
 24                 Console.WriteLine("({0},{1})({2})", edge.startEdge, edge.endEdge, edge.weight);
 25             }
 26 
 27             Console.Read();
 28         }
 29     }
 30 
 31     #region 定义矩阵节点
 32     /// <summary>
 33     /// 定义矩阵节点
 34     /// </summary>
 35     public class MatrixGraph
 36     {
 37         Graph graph = new Graph();
 38 
 39         PriorityQueue<Edge> queue;
 40 
 41         DisjointSet<int> set;
 42 
 43         public class Graph
 44         {
 45             /// <summary>
 46             /// 顶点信息
 47             /// </summary>
 48             public int[] vertexs;
 49 
 50             /// <summary>
 51             /// 边的条数
 52             /// </summary>
 53             public int[,] edges;
 54 
 55             /// <summary>
 56             /// 顶点个数
 57             /// </summary>
 58             public int vertexsNum;
 59 
 60             /// <summary>
 61             /// 边的个数
 62             /// </summary>
 63             public int edgesNum;
 64         }
 65 
 66         #region 矩阵的构建
 67         /// <summary>
 68         /// 矩阵的构建
 69         /// </summary>
 70         public void Build()
 71         {
 72             //顶点数
 73             graph.vertexsNum = 6;
 74 
 75             //边数
 76             graph.edgesNum = 8;
 77 
 78             graph.vertexs = new int[graph.vertexsNum];
 79 
 80             graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
 81 
 82             //构建二维数组
 83             for (int i = 0; i < graph.vertexsNum; i++)
 84             {
 85                 //顶点
 86                 graph.vertexs[i] = i;
 87 
 88                 for (int j = 0; j < graph.vertexsNum; j++)
 89                 {
 90                     graph.edges[i, j] = int.MaxValue;
 91                 }
 92             }
 93 
 94             graph.edges[0, 1] = graph.edges[1, 0] = 80;
 95             graph.edges[0, 3] = graph.edges[3, 0] = 100;
 96             graph.edges[0, 5] = graph.edges[5, 0] = 20;
 97             graph.edges[1, 2] = graph.edges[2, 1] = 90;
 98             graph.edges[2, 5] = graph.edges[5, 2] = 70;
 99             graph.edges[4, 5] = graph.edges[5, 4] = 40;
100             graph.edges[3, 4] = graph.edges[4, 3] = 60;
101             graph.edges[2, 3] = graph.edges[3, 2] = 10;
102 
103             //优先队列,存放树中的边
104             queue = new PriorityQueue<Edge>();
105 
106             //并查集
107             set = new DisjointSet<int>(graph.vertexs);
108 
109             //将对角线读入到优先队列
110             for (int i = 0; i < graph.vertexsNum; i++)
111             {
112                 for (int j = i; j < graph.vertexsNum; j++)
113                 {
114                     //说明该边有权重
115                     if (graph.edges[i, j] != int.MaxValue)
116                     {
117                         queue.Eequeue(new Edge()
118                         {
119                             startEdge = i,
120                             endEdge = j,
121                             weight = graph.edges[i, j]
122                         }, graph.edges[i, j]);
123                     }
124                 }
125             }
126         }
127         #endregion
128 
129         #region 边的信息
130         /// <summary>
131         /// 边的信息
132         /// </summary>
133         public class Edge
134         {
135             //开始边
136             public int startEdge;
137 
138             //结束边
139             public int endEdge;
140 
141             //权重
142             public int weight;
143         }
144         #endregion
145 
146         #region Kruskal算法
147         /// <summary>
148         /// Kruskal算法
149         /// </summary>
150         public List<Edge> Kruskal()
151         {
152             //最后收集到的最小生成树的边
153             List<Edge> list = new List<Edge>();
154 
155             //循环队列
156             while (queue.Count() > 0)
157             {
158                 var edge = queue.Dequeue();
159 
160                 //如果该两点是同一个集合,则剔除该集合
161                 if (set.IsSameSet(edge.t.startEdge, edge.t.endEdge))
162                     continue;
163 
164                 list.Add(edge.t);
165 
166                 //然后将startEdge 和 endEdge Union起来,表示一个集合
167                 set.Union(edge.t.startEdge, edge.t.endEdge);
168 
169                 //如果n个节点有n-1边的时候,此时生成树已经构建完毕,提前退出
170                 if (list.Count == graph.vertexsNum - 1)
171                     break;
172             }
173 
174             return list;
175         }
176         #endregion
177     }
178     #endregion
179 }


推荐文章

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

标签列表
网站分类
最新留言

Powered By Z-BlogPHP and Terry

Copyright @ laigucomputer.com 来古计算机 工信部备案号:粤ICP备18009132号