最小生成树的Prim算法和Kruskal算法--Java

最小生成树:

最小生成树是在无向图中,找到连通的树。该树由无向图中的N个节点构成,由N-1条边构成且不存在回路,且其总价值最低。最小生成树存在的必要条件是当且仅当G是连通的。

Prim算法:

prim算法是选择一步步的让这颗树长成。在每一步,都要把一个节点当做根并往上加边,这样也就把相关联的顶点给加入到树中。算法需要两个数组:lowestCost数组和cloest数组。需要两个顶点集:U和V。U集合表示已经加入到生成树的顶点,V表示还未加入到生成树的顶点。lowestCost数组表示从U中的节点到V中的节点做对应的最小值。比如lowestCost[i] = 3。就表示U集合中的顶点到顶点数组i这个位置代表的顶点的最短距离为3。cloest数组表示该顶点位置的前一个节点的位置,比如cloest[j] = 2就表示在顶点数组中j这个位置的顶点的前一个顶点的位置。因为cloest数组和顶点数组是相对应的。

该算法对于稠密图来说是比较有优势的。因为这是对顶点进行的操作。

算法流程:

  1. 先规定个其实节点i,将cloest中的全部位置都初始化为i。将lowestCost[i]的值初始化为0,0代表这个节点已经被加入到U中了。
  2. 接下来进行循环,找到U中的节点到V中的节点的最小值,更新lowestCost的值。在lowestCost的值中选择个最小的值的位置,然后把这个位置代表的节点加入到U中(也就是把对应的lowestCost的值更新为0)。再把对应的cloest的位置上的值修改为最小变的起点。
  3. 接下来就是重复2,直到所有的cloestCost的值都为0就代表所有的节点都已加入到U中。

无向图的创立和之前的dijkstra算法的创建的是一样的,依然使用邻接表进行存储的。

//最小生成树
	//prim算法,需要个lowestcost数组,表示U->V节点最短的权值
	int[] lowestCost;
	//需要个cloest数组,表示这个节点的前面的节点
	int[] cloest;
	public void prim() {
		lowestCost = new int[vertexNum.length];
		//先初始化为无穷大
		for(int i = 0;i<vertexNum.length;i++) {
			lowestCost[i] = Integer.MAX_VALUE;
		}
		cloest = new int[vertexNum.length];
		String  start = "1";
		int index = getIndex(start);
		vertex[index].visited = true;
		//让节点的开始节点都为这个开始节点
		for(int i = 0;i<cloest.length;i++) {
			cloest[i] = index;
		}
		//把开始节点的临近节点放入lowestCost数组中
		ArrayList<info> infor = vertex[index].adj;
		for(info t:infor) {
			lowestCost[t.index] = t.weight;
		}
		//lowestCost数组数值为0代表访问过
		lowestCost[index] = 0;
		
		//开始一个个的把节点加入到U中
		for(int i = 1;i<vertexNum.length;i++) {
			//首先获取第一个不为0的节点,为0说明已经以这个节点为根访问过
			for(int j = 0;j<vertexNum.length;j++) {
				if(lowestCost[j] != 0) {
					index = j;
					break;
				}
			}
			//找到值最小的节点
			
			for(int j = index;j<vertexNum.length;j++) {
				if(lowestCost[j]!=0 && lowestCost[j]<lowestCost[index]) {
					index = j;
				}
			}
			
			
			//这个位置信息的更改
			int flag = 0;
			for(int k = 0;k<vertexNum.length;k++) {
				if(lowestCost[k] == 0) {
					infor = vertex[k].adj;
					for(info t:infor) {
						if(t.index == index && t.weight == lowestCost[index]) {
							cloest[index] = k;
							flag = 1;
							break;
							
						}
					}
					if(flag == 1) {
						break;
					}
				}
			}
		
			lowestCost[index] = 0;
			
			infor = vertex[index].adj;
			for(info t:infor) {
				if(lowestCost[t.index]!=0 && t.weight < lowestCost[t.index]) {
					lowestCost[t.index] = t.weight;
				}
			}
		}
		
		
	}
	

Kruskal算法

Kruskal算法是基于连续的按照最小边的策略进行生成树的构建。Kruskal算法开始的时候把每个顶点都当做棵树,这样就是个森林,然后选取最小的边进行树的连接,最后生成只含有一棵树。该算法要遵循的有两点:

  1. 每次选取最小的边来构成生成树
  2. 将这条边加入后不能形成闭环

因此此时就得用到并查集的思想了。起初每个顶点都在自己的集合中,当加入一条边的时候就相当于把两个顶点放在了同一个集合里,当加入新边的时候,就要查询这个新边的顶点是不是已经属于一个集合了,如果已经属于一个集合了,此时就要舍弃掉这条边继续寻找新的最小边。如果不属于,就将这两个顶点放在一个新的集合里。所以,此时需要两个数组来进行操作算法:vtex数组用来判断两个顶点是不是属于同一个集合。E数组用来记录两个顶点之间的权值。算法过程为

  1. 首先在E数组里找到最小权值的边的两个顶点,记录下这两个顶点,并将这个数组对应的值置为0(0代表这条变已经被处理过了)
  2. 判断这两个顶点是不是属于一个集合,如果是就舍弃这这条边,如果不是就对这两个顶点进行Union操作
  3. 反复执行1,2操作,直到E数组里全部的值都为0。

该算法适合于边较少的情况。

代码:

//最小生成树  kruskal算法
	public void kruskal() {
		//需要E数组存储边的信息。
		int[][] E = new int[vertexNum.length][vertexNum.length];
		for(int i = 0;i<vertexNum.length;i++) {
			for (int j = 0; j < vertexNum.length; j++) {
				
					E[i][j] = Integer.MAX_VALUE;
				
			}
		}
		for(int i = 0;i<vertex.length;i++) {
			ArrayList<info> infor = vertex[i].adj;
			for(info t:infor) {
				E[i][t.index] = t.weight;
			}
		}
		
		//需要个节点数组进行是不是连接的判断
		
		int[] vtex = new int[vertexNum.length];
		for(int i = 0;i<vertexNum.length;i++) {
			vtex[i] = -1;
		}
		while(true) {
			int left = 0;
			int right = 0;
			for (int i = 0; i < vtex.length; i++) {
				for (int j = 0; j < vtex.length; j++) {
					if(E[i][j] != 0 && E[i][j] < E[left][right]) {
						left = i;
						right = j;
					}
				}
			}
			if(right == 0 && left == 0) {
				break;
			}
			E[left][right] = 0;
			E[right][left] = 0;
			
			if(getRoot(vtex,left) != getRoot(vtex,right)) {
				System.out.println("边:"+vertex[left].number+"-> "+vertex[right].number);
				union(vtex,left,right);
			}
			
		}
		for(int i = 0;i<vertexNum.length;i++) {
			System.out.println(vtex[i]);
		}
		
	}
	
	private int getRoot(int[] vtex,int x) {
		if(vtex[x] == -1) {
			return x;
		}
		else
			return getRoot(vtex,vtex[x]);
	}
	
	private void union(int[] vtex,int left,int right) {
		right = getRoot(vtex,right);
		vtex[right] = left;
	}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值