最小生成树的java实现
文章目录
笔记来源:中国大学MOOC王道考研
一、概念
连通图:图中任意两点都是连通的,那么图被称作连通图
生成树:连通图包含全部顶点的一个极小连通子图
最小生成树:在含有n个顶点的带权无向连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树(不一定唯一)。
- 性质1:不一定唯一
 - 性质2:如果所有边的权重都不相同,则一定唯一
 - 性质3:如果连通图只有n-1条边,则最小生成树就是它本身
 - 性质4:最小生成树的边数为n-1
 
二、算法
2.1 Prim算法
步骤如下:
初始化,取任意顶点加入结果树:
加入A相邻的且不在结果树中,并且是最小权值的点C
加入与A,C相邻的且不在结果树中,并且是最小权值的点B(BC最小)
重复上述步骤,直到所有顶点都进入结果树:
java代码实现如下:
我们需要用两个数组来实现过程:
- min_weight[n]:当前结果树到所有顶点的最短距离
 - adjvex[n]:adjvex[C]=0,代表C是通过A加入结果树的(0是A的下标)
 
  /*
  * 首先我们给出图的存储结构
  */
package MST;
import java.util.List;
public class Graph {
	/*
	 * 点的存储
	 */
	private List<String> vex;
	/*
	 * 边的存储
	 */
	private int edges[][];
	public Graph(List<String> vex, int[][] edges) {
		this.vex = vex;
		this.edges = edges;
	}
	public List<String> getVex() {
		return vex;
	}
	public void setVex(List<String> vex) {
		this.vex = vex;
	}
	public int[][] getEdges() {
		return edges;
	}
	public void setEdges(int edges[][]) {
		this.edges = edges;
	}
	public int getVexNum() {
		return vex.size();
	}
	public int getEdgeNum() {
		return edges.length;
	}
}
然后初始化图:
public class Prime {
	int m = Integer.MAX_VALUE;
	int[][] edges = {
	        {0, 3, 1, m, 4},
	        {3, 0, 2, m, m},
	        {1, 2, 0, 5, 6},
	        {m, m, 5, 0, m},
	        {4, m, 6, m, 0},
		};
    //打印最小生成树
	void MST_Prime(Graph G) {
		int vexNum = G.getVexNum();//节点个数
		int[] min_weight = new int[vexNum];//当前结果树到所有顶点的最短距离
		int[] adjvex = new int[vexNum];//adjvex[C]=0,代表C是通过A加入结果树的(0是A的下标)
		/*初始化两个辅助数组*/
		for(int i = 0; i < vexNum; i++) {
			min_weight[i] = (G.getEdges())[0][i];//第一个顶点到其余顶点的距离
			adjvex[i]=0;
		}
		int min_edg;//当前挑选的最小权值
		int min_vex = 0;//最小权值对应的节点下标
		/*循环剩余n-1个点*/
		for(int i = 1; i < vexNum; i++) {
			min_edg = Integer.MAX_VALUE;
			for(int j = 1; j < vexNum; j++) {
				if(min_weight[j]!=0 && min_weight[j] < min_edg) {
					//寻找还没有被挑选进来的,最小权重的点
					min_edg = min_weight[j];
					min_vex = j;
				}
			}
			min_weight[min_vex] = 0;//纳入结果树
			/*修改对应辅助数组的值*/
			for(int j = 0; j < vexNum; j++) {
				if(min_weight[j]!=0 && (G.getEdges())[min_vex][j]<min_weight[j] && (G.getEdges())[min_vex][j]>0) {
					min_weight[j] = (G.getEdges())[min_vex][j];
					adjvex[j]=min_vex;
				}
			}
			int pre = adjvex[min_vex];
			int end = min_vex;
			System.out.println("("+G.getVex().get(pre)+","+G.getVex().get(end)+")");
		}
	}
	//初始化图
	Graph init() {
		List<String> vex=new ArrayList<String>();
		vex.add("A");
		vex.add("B");
		vex.add("C");
		vex.add("D");
		vex.add("E");
		Graph graph = new Graph(vex, edges);
		return graph;
	}
	public static void main(String[] args) {
		Prime prime = new Prime();
		Graph graph = prime.init();
		prime.MST_Prime(graph);
	}
}
打印结果如下:
(A,C)
(C,B)
(A,E)
(C,D)
2.2 Kruskal算法
步骤如下:
每个顶点都是独立的树
挑选最短的边AC,加入边集中
依次加入BC,AB,但是AB构成了回路,舍弃
重复直到取了n-1条边
java代码实现如下:
使用 并查集、堆排序、kruskal算法
引用并查集博客:Java实现并查集
//首先我们实现并查集(用来判断是否构成回路--是否属于一个并查集)
public class UnionFindSet {
	//查询树的根
	public static int find(int x, int [] par){
		if(par[x] == x){
			return x;
		}else{
			//压缩路径,第二次查询可以直接返回x的根而不用递归
			return par[x] = find(par[x], par);
		}
	}
	//合并
	public static void unite(int x, int y, int [] par, int [] rank){
		x = find(x, par);
		y = find(y, par);
		if(x == y){
			return ;
		}
		if(rank[x] < rank[y]){
			par[x] = y;
		}else{
			par[y] = x;
			if(rank[x] == rank[y]) rank[x]++;
		}
	}
	//判断x和y是否属于同一个集合
	public static boolean same(int x, int y, int [] par){
		return find(x, par) == find(y, par);
	}
}
然后实现堆排序(稍作修改):
堆排序参考这篇博客:Java实现堆排序和图解
public class HeapSort {
   public static void sort(Edge[] arr){
       //1.构建大顶堆
       for(int i=arr.length/2-1;i>=0;i--){
           //从第一个非叶子结点从下至上,从右至左调整结构
           adjustHeap(arr,i,arr.length);
       }
       //2.调整堆结构+交换堆顶元素与末尾元素
       for(int j=arr.length-1;j>0;j--){
           swap(arr,0,j);//将堆顶元素与末尾元素进行交换
           adjustHeap(arr,0,j);//重新对堆进行调整
       }
   }
   /**
    * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
    * @param arr
    * @param i
    * @param length
    */
   public static void adjustHeap(Edge[] arr,int i,int length){
       Edge temp = arr[i];//先取出当前元素i
       for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
           if(k+1<length && arr[k].weight<arr[k+1].weight){//如果左子结点小于右子结点,k指向右子结点
               k++;
           }
           if(arr[k].weight >temp.weight){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
               arr[i] = arr[k];
               i = k;
           }else{
               break;
           }
       }
       arr[i] = temp;//将temp值放到最终的位置
   }
   /**
    * 交换元素
    * @param arr
    * @param a
    * @param b
    */
   public static void swap(Edge[] arr,int a ,int b){
       Edge temp=arr[a];
       arr[a] = arr[b];
       arr[b] = temp;
   }
}
最后我们实现Kruskal算法:
package MST;
import java.util.ArrayList;
import java.util.List;
public class Kruskal {
	int m = Integer.MAX_VALUE;
	int[][] arr = {
	        {0, 3, 1, m, 4},
	        {3, 0, 2, m, m},
	        {1, 2, 0, 5, 6},
	        {m, m, 5, 0, m},
	        {4, m, 6, m, 0},
		};
	Graph init() {
		List<String> vex=new ArrayList<String>();
		vex.add("A");
		vex.add("B");
		vex.add("C");
		vex.add("D");
		vex.add("E");
		Graph graph = new Graph(vex, arr);
		return graph;
	}
	//kruskal算法
	void MST_Kruskal(Graph G, Edge[] edges, int[] parents, int[] rank) {
		HeapSort.sort(edges);//堆排序
		for(int i = 0; i < G.getEdgeNum(); i++) {
			if(!UnionFindSet.same(edges[i].a, edges[i].b, parents)) {
				UnionFindSet.unite(edges[i].a, edges[i].b, parents, rank);
				System.out.println("("+G.getVex().get(edges[i].a)+","+G.getVex().get(edges[i].b)+")");
			}
		}
	}
	public static void main(String[] args) {
		Kruskal kruskal = new Kruskal();
		Graph graph = kruskal.init();
		int[] parents = {0,1,2,3,4};
		int[] rank = {1,1,1,1,1};
		Edge[] edges = new Edge[10];
		int index = 0;
		for(int i = 0; i < 5;i++) {
			for(int j=0;j<i;j++) {
				edges[index] = new Edge();
				edges[index].weight = kruskal.arr[i][j];
				edges[index].a = i;
				edges[index++].b = j;
			}
		}
		kruskal.MST_Kruskal(graph, edges, parents, rank);
	}
}
输出结构为:
(C,A)
(C,B)
(E,A)
(D,C)
最小生成树的java实现的更多相关文章
- 图的最小生成树(java实现)
		
1.图的最小生成树(贪心算法) 我两个算法的输出都是数组表示的,当前的索引值和当前索引对应的数据就是通路,比如parent[2] = 5;即2和5之间有一个通路,第二个可能比较好理解,第一个有点混乱 ...
 - 最小生成树之kruskal方法实现 (java)
		
今天是个阴天,下了点雨,work ......... 步骤:将所有边排序,然后不断从小到大加上边,这个过程最重要的是避免环的产生,此处用并查集.(nyoj 38) package 最小生成树; imp ...
 - java实现最小生成树的prim算法和kruskal算法
		
在边赋权图中,权值总和最小的生成树称为最小生成树.构造最小生成树有两种算法,分别是prim算法和kruskal算法.在边赋权图中,如下图所示: 在上述赋权图中,可以看到图的顶点编号和顶点之间邻接边的权 ...
 - 【惊喜】Github爆火的java面试神技+java核心面试技术已开发下载,大厂内都传疯了!
		
前言 今年,由于疫情的影响,很多互联网企业都在缩减招聘成本.作为程序员,原本这两年就面临竞争激烈.年龄危机的问题,而现在的求职局面又完全是企业在挑人的状态. 所以最好能在空闲的时候看看大厂相匹配的技术 ...
 - Java Web学习之路
		
编程基础 1-1 常用数据结构 数组.链表.堆.栈.队列.Hash表.二叉树等1-2 算法思想 算法时间复杂度和空间复杂度的分析计算 1-2 算法思想:递推.递归.穷举.贪心.分治.动态规划.迭代.分 ...
 - Spark案例分析
		
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
 - 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径1
		
import java.util.ArrayList; import java.util.List; // 模块E public class AdjMatrixGraph<E> { pro ...
 - 图的存储,搜索,遍历,广度优先算法和深度优先算法,最小生成树-Java实现
		
1)用邻接矩阵方式进行图的存储.如果一个图有n个节点,则可以用n*n的二维数组来存储图中的各个节点关系. 对上面图中各个节点分别编号,ABCDEF分别设置为012345.那么AB AC AD 关系可以 ...
 - java用Kruskal实现最小生成树
		
今天更新这篇文章超级激动,因为我会最小生成树的算法了(其实昨天就开始研究了,只是昨天参加牛客网的算法比赛,结果又被虐了,好难过~) 最小生成树的算法,其实学了数据结构就会有一定的基础,Kruskal算 ...
 
随机推荐
- Jmeter(十二) - 从入门到精通 - JMeter逻辑控制器 - 终篇(详解教程)
			
1.简介 Jmeter官网对逻辑控制器的解释是:“Logic Controllers determine the order in which Samplers are processed.”. 意思 ...
 - java程序员软件测试技巧
			
测试是开发的一个非常重要的方面,可以在很大程度上决定一个应用程序的命运.良好的测试可以在早期捕获导致应用程序崩溃的问题,但较差的测试往往总是导致故障和停机. 测试是开发的一个非常重要的方面,可以在很大 ...
 - xdebug调试代码常用操作
			
xdebug调试代码常用操作 1.查看变量中的值 2.常用快捷键 ①F8单步调试 ②F9可以直接快速结束调试 ③F7 可以进入调试代码的底层方法,我觉得查看底层代码时,这个特别的方便!
 - 暑假集训Day2 互不侵犯(状压dp)
			
这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...
 - Data Science and Matrix Optimization-课程推荐
			
课程介绍:Data science is a "concept to unify statistics, data analysis, machine learning and their ...
 - Oracle安装完成后修改服务器机器名,Oracle部分服务无法启动
			
Oracle安装完成后修改服务器机器名,Windows server 2012 R2系统提示Oracle 11g下面3个服务无法启动: OracleDBConsoleorcl OracleOraDb1 ...
 - css3常见好看的投影效果_css3阴影box-shadow高大上用法
			
在设计图会经常使用到的阴影效果,如何用css3来实现呢?这篇文章主要整理box-shadow的一些好看常用的投影效果. 1.曲面/椭圆投影效果: 代码如下: <style> .shadow ...
 - CSS3 nth-child的使用,详解css中nth的作用,以及nth-child的兼容写法
			
:nth-child是css3的一个比较常用的选择器.它用于匹配属于其父元素中的子元素,不论元素的类型. 它的参数可以是数字.关键词或公式.下面讲介绍它的使用方法, nth-child的使用 html ...
 - mysql 常见使用场景
			
场景1:表中多条相同账户数据,根据创建日期排序取第一条. select * from cms_client_rpq rpq where id = ( select id from cms_client ...
 - jira仪表盘的建立与共享
			
一般在项目测试阶段,可以通过jira仪表盘清晰的展示bug的各个状态的数量,各个开发人员的bug数量. 有效督促开发解决问题. 也为测试日报提供了良好的数据支持,减少人工统计的工作量. 1.建议筛选器 ...