zoukankan      html  css  js  c++  java
  • 常用十大算法(七)— 克鲁斯卡尔算法

    常用十大算法(七)— 克鲁斯卡尔算法

    博客说明

    文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!

    介绍

    • 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。

    最小生成树

    • 最小生成树(Minimum Cost Spanning Tree),简称MST。
    • 给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树
    • N个顶点,一定有N-1条边
    • 包含全部顶点
    • N-1条边都在图中
    • 求最小生成树的算法主要是普里姆 算法和克鲁斯卡尔算法

    修路问题

    image-20200906121921500

    • 有北京有新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通
    • 各个站点的距离用边线表示(权) ,比如 A – B 距离 12公里
    • 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

    思路

    • 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
    • 具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
    问题一

    排序

    问题二

    判断回路

    代码实现
    package com.atguigu.kruskal;
    
    import java.util.Arrays;
    
    public class KruskalCase {
    
    	private int edgeNum; //边的个数
    	private char[] vertexs; //顶点数组
    	private int[][] matrix; //邻接矩阵
    	//表示不联通
    	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 = new KruskalCase(vertexs, matrix);
    	  kruskalCase.print();
    	  kruskalCase.kruskal(); 
    	}
    	
    	//构造器
    	public KruskalCase(char[] vertexs, int[][] matrix) {
    		int vlen = vertexs.length;
    		
    		//复制拷贝
    		this.vertexs = new char[vlen];
    		for(int i = 0; i < vertexs.length; i++) {
    			this.vertexs[i] = vertexs[i];
    		}
    		
    		//初始化表
    		this.matrix = new int[vlen][vlen];
    		for(int i = 0; i < vlen; i++) {
    			for(int j= 0; j < vlen; j++) {
    				this.matrix[i][j] = matrix[i][j];
    			}
    		}
    
    		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];
    		
    		EData[] edges = getEdges();
    		System.out.println("图的边的集合=" + Arrays.toString(edges) + "共"+ edges.length); //12    
    		
    		//排序
    		sortEdges(edges);
    		
        //遍历数组
    		for(int i=0; i < edgeNum; i++) {
    			int p1 = getPosition(edges[i].start);
    			int p2 = getPosition(edges[i].end);
    			int m = getEnd(ends, p1);
    			int n = getEnd(ends, p2);
    			if(m != n) {
    				ends[m] = n;
    				rets[index++] = edges[i];
    			}
    		}
    		System.out.println("×îСÉú³ÉÊ÷Ϊ");
    		for(int i = 0; i < index; i++) {
    			System.out.println(rets[i]);
    		}
    	}
    	
    
    	public void print() {
    		System.out.println("邻接矩阵: 
    ");
    		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();
    		}
    	}
    
    	//对边排序
    	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;
    				}
    			}
     		}
    	}
    	
      //顶点的值
    	private int getPosition(char ch) {
    		for(int i = 0; i < vertexs.length; i++) {
    			if(vertexs[i] == ch) {
    				return i;
    			}
    		}
    		return -1;
    	}
      
    	//获取图中的边
    	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;
    	}
    	
      //获取终点
    	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 + ", " + end + ">= " + weight + "]";
    	}
    }
    
    

    感谢

    尚硅谷

    以及勤劳的自己,个人博客GitHub

    微信公众号

  • 相关阅读:
    [剑指 Offer 11. 旋转数组的最小数字]
    进程描述符(PCB)
    [剑指 Offer 57. 和为s的两个数字]
    Linux netstat命令
    kafka2.3.X配置文件
    docker
    shell操作mysql数据库
    Linux文件查找之find命令
    sed 切割日志文件
    Linux文本处理之awk
  • 原文地址:https://www.cnblogs.com/guizimo/p/13623067.html
Copyright © 2011-2022 走看看