Kruskal algorithm (java)

1. Introduction to Kruskal algorithm

  Kruskal's algorithm is an algorithm for constructing a minimum spanning tree. The time complexity is O ( ∣ E ∣ log ∣ E ∣ ) O(|E|log|E|)O ( E l o g E ) . Kruskal's algorithm is suitable for graphs with sparse edges and many vertices.

2. Principle of Kruskal algorithm

  (1) Initially, it is a non-connected graph T with only n vertices but no edges, and each vertex forms a connected component by itself.
  (2) According to the order of edge weights from small to large, continuously select the edge that has not been selected and has the smallest weight. If the vertex attached to the edge falls on a different connected component in T, then add this edge to T, otherwise discard this edge and choose the next edge with the smallest weight. In other words, the joined edges cannot be connected to form a cycle.
  (3) Repeat step (2) until all vertices in T are on a connected component.

3. Diagram of Kruskal algorithm

insert image description here
  The process of constructing the minimum spanning tree of graph (a) is as follows:
  (1) Sort the edges according to their weights:

< V 1,V 3 V_{1},V_{3}V1V3>:1
< V 4 , V 6 V_{4},V_{6} V4V6>:2
< V 2 , V 5 V_{2},V_{5} V2V5>:3
< V 3 , V 6 V_{3},V_{6} V3V6>:4
< V 1 , V 4 V_{1},V_{4} V1V4>:5
< V 2 , V 3 V_{2},V_{3} V2V3>:5
< V 3 , V 4 V_{3},V_{4} V3V4>:5
< V 1 , V 2 V_{1},V_{2} V1V2>:6
< V 3 , V 5 V_{3},V_{5} V3V5>:6
< V 5 , V 6 V_{5},V_{6} V5V6>:6

  (2) First, set < V 1 , V 3 V_{1}, V_{3}V1V3>Join the minimum spanning tree. (As shown in Figure b)
  (3) Will < V 4 , V 6 V_{4}, V_{6}V4V6>Join the minimum spanning tree. (As shown in Figure c)
  (4) Will < V 2 , V 5 V_{2}, V_{5}V2V5>Join the minimum spanning tree. (As shown in Figure d)
  (5) Will < V 3 , V 6 V_{3}, V_{6}V3V6>Join the minimum spanning tree. (As shown in Figure e)
  (6) The side with the minimum weight at this time is: < V 1 , V 4 V_{1}, V_{4}V1V4>、< V 2 , V 3 V_{2},V_{3} V2V3>、< V 3 , V 4 V_{3},V_{4} V3V4>, since < V 1 , V 4 V_{1}, V_{4}V1V4>、< V 3 , V 4 V_{3},V_{4} V3V4> into a ring, so < V 2 , V 3 V_{2}, V_{3}V2V3>Join the minimum spanning tree. (As shown in Figure f)
  (7) The number of edges = the number of vertices - 1, and the minimum spanning tree is constructed.

4. Kruskal algorithm code implementation

package com.haiyang.algorithm.kruskal;

import java.util.Arrays;

public class KruskalCase {
    
    

	private int edgeNum; //边的个数
	private char[] vertexs; //顶点数组
	private int[][] matrix; //邻接矩阵
	//使用 INF 表示两个顶点不能连通
	private static final int INF = Integer.MAX_VALUE;

	public static void main(String[] args) {
    
    
		char[] vertexs = {
    
    'A', 'B', 'C', 'D', 'E', 'F', 'G'};
		//克鲁斯卡尔算法的邻接矩阵
		int matrix[][] = {
    
    
				/*A*//*B*//*C*//*D*//*E*//*F*//*G*/
				/*A*/ {
    
       0,  12, INF, INF, INF,  16,  14},
				/*B*/ {
    
      12,   0,  10, INF, INF,   7, INF},
				/*C*/ {
    
     INF,  10,   0,   3,   5,   6, INF},
				/*D*/ {
    
     INF, INF,   3,   0,   4, INF, INF},
				/*E*/ {
    
     INF, INF,   5,   4,   0,   2,   8},
				/*F*/ {
    
      16,   7,   6, INF,   2,   0,   9},
				/*G*/ {
    
      14, INF, INF, INF,   8,   9,   0}};

		//创建KruskalCase 对象实例
		KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
		//输出构建的
		kruskalCase.print();
		kruskalCase.kruskal();

	}

	//构造器
	public KruskalCase(char[] vertexs, int[][] matrix) {
    
    
		//初始化顶点数和边的个数
		int vlen = vertexs.length;
		//初始化顶点, 复制拷贝的方式
		this.vertexs = vertexs;
		//初始化边, 使用的是复制拷贝的方式
		this.matrix = matrix;
		//统计边的条数
		for(int i =0; i < vlen; i++) {
    
    
			for(int j = i+1; j < vlen; j++) {
    
    
				if(this.matrix[i][j] != INF) {
    
    
					edgeNum++;
				}
			}
		}

	}
	public void kruskal() {
    
    
		int index = 0; //表示最后结果数组的索引
		int[] ends = new int[edgeNum]; //用于保存"已有最小生成树" 中的每个顶点在最小生成树中的终点
		//创建结果数组, 保存最后的最小生成树
		EData[] rets = new EData[edgeNum];

		//获取图中 所有的边的集合 , 一共有12边
		EData[] edges = getEdges();
		System.out.println("图的边的集合=" + Arrays.toString(edges) + " 共"+ edges.length); //12

		//按照边的权值大小进行排序(从小到大)
		sortEdges(edges);

		//遍历edges 数组,将边添加到最小生成树中时,判断是准备加入的边否形成了回路,如果没有,就加入 rets, 否则不能加入
		for(int i=0; i < edgeNum; i++) {
    
    
			//获取到第i条边的第一个顶点(起点)
			int p1 = getPosition(edges[i].start); //p1=4
			//获取到第i条边的第2个顶点
			int p2 = getPosition(edges[i].end); //p2 = 5

			//获取p1这个顶点在已有最小生成树中的终点
			int m = getEnd(ends, p1); //m = 4
			//获取p2这个顶点在已有最小生成树中的终点
			int n = getEnd(ends, p2); // n = 5
			//是否构成回路
			if(m != n) {
    
     //没有构成回路
				ends[m] = n; // 设置m 在"已有最小生成树"中的终点 <E,F> [0,0,0,0,5,0,0,0,0,0,0,0]
				rets[index++] = edges[i]; //有一条边加入到rets数组
			}
		}
		//<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。
		//统计并打印 "最小生成树", 输出  rets
		System.out.println("最小生成树为");
		for(int i = 0; i < index; i++) {
    
    
			System.out.println(rets[i]);
		}


	}

	//打印邻接矩阵
	public void print() {
    
    
		System.out.println("邻接矩阵为: \n");
		for(int i = 0; i < vertexs.length; i++) {
    
    
			for(int j=0; j < vertexs.length; j++) {
    
    
				System.out.printf("%12d", matrix[i][j]);
			}
			System.out.println();//换行
		}
	}

	/**
	 * 功能:对边进行排序处理, 冒泡排序
	 * @param edges 边的集合
	 */
	private void sortEdges(EData[] edges) {
    
    
		for(int i = 0; i < edges.length - 1; i++) {
    
    
			for(int j = 0; j < edges.length - 1 - i; j++) {
    
    
				if(edges[j].weight > edges[j+1].weight) {
    
    //交换
					EData tmp = edges[j];
					edges[j] = edges[j+1];
					edges[j+1] = tmp;
				}
			}
		}
	}
	/**
	 *
	 * @param ch 顶点的值,比如'A','B'
	 * @return 返回ch顶点对应的下标,如果找不到,返回-1
	 */
	private int getPosition(char ch) {
    
    
		for(int i = 0; i < vertexs.length; i++) {
    
    
			if(vertexs[i] == ch) {
    
    //找到
				return i;
			}
		}
		//找不到,返回-1
		return -1;
	}
	/**
	 * 功能: 获取图中边,放到EData[] 数组中,后面我们需要遍历该数组
	 * 是通过matrix 邻接矩阵来获取
	 * EData[] 形式 [['A','B', 12], ['B','F',7], .....]
	 * @return
	 */
	private 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] != INF) {
    
    
					edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
				}
			}
		}
		return edges;
	}
	/**
	 * 功能: 获取下标为i的顶点的终点(), 用于后面判断两个顶点的终点是否相同
	 * @param ends : 数组就是记录了各个顶点对应的终点是哪个,ends 数组是在遍历过程中,逐步形成
	 * @param i : 表示传入的顶点对应的下标
	 * @return 返回的就是 下标为i的这个顶点对应的终点的下标,
	 */
	private int getEnd(int[] ends, int i) {
    
     // i = 4 [0,0,0,0,5,0,0,0,0,0,0,0]
		while(ends[i] != 0) {
    
    
			i = ends[i];
		}
		return i;
	}

}

//创建一个类EData 表示一条边的数据类
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 + ", " + end + ">= " + weight + "]";
	}

}

Guess you like

Origin blog.csdn.net/weixin_43698993/article/details/122770503