**
克鲁斯卡尔算法
**
克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同,它的时间复杂度为O(eloge)(e为网中的边数),所以,适合于求边稀疏的网的最小生成树 。
克鲁斯卡尔(Kruskal)算法从另一途径求网的最小生成树。其基本思想是:假设连通网G=(V,E),令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。依此类推,直至T中所有顶点构成一个连通分量为止
例:
思路:
实现代码:
package com.algorithm;
import java.util.Arrays;
public class KruskaAlgorithm {
private char[] vertxs;//创建顶点数组
private int[][] martix;//创建邻接矩阵
private int edgesNum;//定义边的个数
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertex = {'A','B','C','D','E','F','G'};
int[][] martix ={
{0,12,INF,INF,INF,16,14},
{12,0,10,INF,INF,7,INF},
{INF,10,0,3,5,6,INF},
{INF,INF,3,0,4,INF,INF},
{INF,INF,5,4,0,2,8},
{16,7,6,INF,2,0,9},
{14,INF,INF,INF,8,9,0}
};
KruskaAlgorithm kruskaAlgorithm = new KruskaAlgorithm(vertex, martix);
kruskaAlgorithm.display();
EData[] edges1 = kruskaAlgorithm.getEdges();
kruskaAlgorithm.kruskal();
}
//创建Kruskal算法
public void kruskal()
{
int index = 0;//定义一个数组索引
int[] ends = new int[edgesNum];//用于保存已有的最小生成树的每个顶点在最小生成树中的终点
EData[] res = new EData[edgesNum];//创建结果数组,用于保存最后的最小生成树
//获取图中的所有边的结合
EData[] edges = getEdges();
//按照边的权值进行排序
sort(edges);
//遍历edges数组,将边添加到最小生成树中,并且判断下次准备加入的边是否构成了回路,没有才可以加入
for (int i = 0; i < edgesNum; i++) {
//获取第i条边的起点
int p1 = getPosition(edges[i].start);
//获取第i条边的终点
int p2 = getPosition(edges[i].end);
//获取p1顶点在已有最小生成树的终点
int m = getEnd(ends,p1);
//获取p2顶点在已有最小生成树的终点
int n = getEnd(ends,p2);
//判断是否构成回路
if (m != n)//此时没有构成回路
{
ends[m] = n;
res[index++] = edges[i];
}
}
for (int i = 0; i < index; i++) {
System.out.println(res[i]);
}
}
//创建构造器来初始化邻接矩阵
public KruskaAlgorithm(char[] vertxs,int[][] martix)
{
int vLen = vertxs.length;
this.vertxs = new char[vLen];
//初始化顶点vertxs
for (int i = 0; i < vLen; i++) {
this.vertxs[i] = vertxs[i];
}
this.martix = new int[vLen][vLen];
//初始化邻接矩阵
for (int i = 0; i < vLen; i++) {
for (int j = 0; j < vLen; j++) {
this.martix[i][j] = martix[i][j];
}
}
//统计有多少条边
for (int i = 0; i < vLen; i++) {
for (int j = i + 1; j < vLen; j++) {
if (this.martix[i][j] != INF )
edgesNum++;
}
}
}
//打印邻接矩阵
public void display()
{
System.out.println("打印邻接矩阵:");
for (int i = 0; i < vertxs.length; i++) {
for (int j = 0; j < vertxs.length; j++) {
System.out.printf("%14d", martix[i][j]);
}
System.out.println();
}
}
//对每条边的权值进行冒泡排序
public void sort(EData[] eData)
{
EData tmp;
for (int i = 0; i < eData.length - 1; i++) {
for (int j = 0; j < eData.length - 1 -i; j++) {
if (eData[i].weight < eData[j].weight)
{
tmp = eData[i];
eData[i] = eData[j];
eData[j] = tmp;
}
}
}
}
//寻找顶点的对应的索引
private int getPosition(char ch)
{
for (int i = 0; i < vertxs.length; i++) {
if (vertxs[i] == ch)
return i;
}
return -1;
}
//寻找每条边的,放入数组EData中,
private EData[] getEdges()
{
int index = 0;//建立索引,方便等下进行存储操作
EData[] eData = new EData[edgesNum];//新建EData数组
//遍历操作每条边,当边存在时,保存
for (int i = 0; i < vertxs.length; i++) {
for (int j = i + 1; j < vertxs.length; j++) {
if (martix[i][j] != INF)
{
eData[index++] = new EData(vertxs[i],vertxs[j],martix[i][j]);
}
}
}
return eData;
}
/*获取下标为i的顶点的终点
用于判断后面两个顶点的终点是否相同
ends(每次加入的ends是动态加入的,记录各个顶点的终点是哪个)
i:表示传入顶点对应的下标
返回的是下标为i的这个顶点对应的终点的下标
*/
private int getEnd(int[] ends,int i)
{
while (ends[i] != 0)
i = ends[i];
return i;
}
}
class EData
{
char start;//边上的起点
char end;//边上的终点
int weight;//此边的权值
public EData(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "EData{" +
"start=" + start +
", end=" + end +
", weight=" + weight +
'}';
}
}