克鲁斯卡尔算法介绍
- 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法
- 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
问题描述

某城市新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通
- 各个站点的距离用边线表示(权) ,比如 A – B 距离 12公里
- 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?
图解
- 把边的权值排好序,选择权值最小的一条边,
[2]

- 继续选择剩余的边,权值最小的,
[3]

- 继续选择剩余权值最小的边,
[4]

- 下一个权值最小的是
[5],但是C的终点是F,E的终点也是F,也就是说CE的终点相同,构成回路,所以不能选择CE - 继续下一个,
[6],同样,CF的终点都是F,构成回路 - 下一个
[7],BF的终点不相同,不构成回路,所以选择BF

- 如上的选择方式同理,选择剩下的部分
代码
using System;using System.Collections.Generic;using System.Globalization;using System.IO;using System.Runtime.Serialization.Formatters.Binary;using System.Text;namespace ConsoleApp1{class Program{static void Main(string[] args){char[] vertexs = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };//邻接矩阵的关系使用二维数组//比如C和G之间不连通,就用8888来表示,权值太大就不考虑了int x = 8888;int[,] matrix = new int[,]{//A B C D E F G/*A*/{0 , 12, x , x , x , 16, 14 },/*B*/{12, 0 , 10, x , x , 7 , x },/*C*/{x , 10, 0 , 3 , 5 , 6 , x },/*D*/{x , x , 3 , 0 , 4 , x , x },/*E*/{x , x , 5 , 4 , 0 , 2 , 8 },/*F*/{16, 7 , 6 , x , 2 , 0 , 9 },/*G*/{14, x , x , x , 8 , 9 , 0 }};KruskalCase kruskal=new KruskalCase(vertexs, matrix);kruskal.print();Console.WriteLine("没排序");EData[] edatas = kruskal.getEdges();foreach(var edata in edatas){edata.print();}Console.WriteLine("排序完");kruskal.sortEdges(edatas);foreach (var edata in edatas){edata.print();}kruskal.kruskal();}}class KruskalCase{private int edgeNum;//边的个数private char[] vertexs;//顶点数组private int[,] matrix;//邻接矩阵public KruskalCase(char[] vertexs,int[,] matrix){this.vertexs = vertexs;this.matrix = matrix;//统计边的数量,j=i+1,自己跟自己就没有必要再进行连接了for(int i = 0; i < matrix.GetLength(0); i++){for(int j = i+1; j < matrix.GetLength(1); j++){if (this.matrix[i, j] != 8888){edgeNum++;}}}}//克鲁斯卡尔算法public void kruskal(){int index = 0;//最后结果数组的索引int[] ends = new int[vertexs.Length];//用于保存 "已有最小生成" 中的每个顶点在最小生成树中的终点//创建结果数组,保存最后的最小生成树EData[] rets=new EData[vertexs.Length-1];//获取图中所有边的集合EData[] edges = getEdges();//按照边的权值大小排序sortEdges(edges);//遍历edges数组,将边添加到最小生成树中时,判断准备加入的边是否构成了回路,如果没有就加入rets,否则就不能加入for(int i = 0; i < edgeNum; i++){//获取到第i条边的第一个顶点(起点),第二个顶点(终点)int p1 = getPosition(edges[i].start);int p2 = getPosition(edges[i].end);//获取p1,p2这个顶点在已有的最小生成树中的终点int m = getEnd(ends, p1);int n = getEnd(ends, p2);//判断是否构成回路if (m != n)//两条边的终点不一样就没有构成回路{ends[m] = n;//设置m在已有最小生成树的终点rets[index++] = edges[i];//有一条边入选了}}Console.WriteLine("最小生成树");foreach (var ret in rets){ret.print();}}//打印邻接矩阵public void print(){for (int i = 0; i < matrix.GetLength(0); i++){for (int j = 0; j < matrix.GetLength(1); j++)Console.Write(matrix[i, j] + " ");Console.WriteLine();}}//对边进行排序处理,这里用冒泡public void sortEdges(EData[] edges){for(int i = 0; i < edges.Length; i++){for (int j = 0; j < edges.Length - 1 - i; j++){if (edges[j].weight > edges[j + 1].weight)//交换{EData temp = edges[j];edges[j] = edges[j + 1];edges[j+1] = temp; ;}}}}//返回ch顶点对应的下标,找不到返回-1public int getPosition(char ch){for(int i =0;i<vertexs.Length;i++)if (vertexs[i] == ch)return i;return -1;}//获取图中的边,放到EData[]数组中,后面需要遍历该数组//通过matrix邻接矩阵来获取public EData[] getEdges(){int index = 0;EData[] edges = new EData[edgeNum];for(int i = 0; i < vertexs.Length; i++){for(int j = i + 1; j < vertexs.Length; j++){if (matrix[i, j] != 8888){edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i, j]);}}}return edges;}//获取下标为i的顶点的终点,用于后面判断两个顶点的终点是否相同//ends记录了各个顶点对应的终点是哪个,ends是在遍历过程中逐步行程的//i表示传入的顶点对应的下标//返回i这个顶点对应的终点的下标public int getEnd(int[] ends,int i){while (ends[i] != 0)i = ends[i];return i;/** 一开始ends这个数组是这样的[0,0,0,0,0,0,0]* i表示传入的顶点的下标,如果这个下标为0,说明这个顶点还没有终点,为0都不进while,直接返回这个下标* 比如EF这条边,* m = E这个点下标为4,最开始传入的时候ends[4]==0,所以直接返回4出去* n = F这个点下标为5,直接返回5* m和n不相同,所以不构成回路,这条边可以加入,加入后,就要设置ends,ends[4]=5* [0,0,0,0,5,0,0]* 表示什么意思,第4个顶点,就是E的终点是5,5指向F,所以E的终点是F。* 下次检测的时候,如果传入的是4,返现ends[4]!=0,说明设置了终点,就把终点的下标赋值给当前下标,循环检测*/}}//边class EData{public char start;//边的一个点public char end;//边的另一个点public int weight;//边的权值public EData(char start,char end,int weight){this.start = start;this.end = end;this.weight = weight;}public void print(){Console.WriteLine($"起始{start},结束{end},权值{weight}");}}}

