prim算法和Kruskal算法

文章目录


一、prim算法

1.基本介绍

普利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图
普利姆的算法如下:
1.设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点集合,E,D是边的集合
2.若从顶点u开始构造最小生成树,则从集合V中取出顶点u放入集合u中,标记顶点v的visited[u]=1
3.若集合u中顶点ui与集合v-U中的顶点vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点vj加入集合U中,将边(ui,vj)加入集合D中,标记visited[vj]=1
4.重复步骤②,直到u与v相等,即所有顶点都被标记为访问过,此时D中有n-1条边

2.应用场景——修路问题

prim算法和Kruskal算法

3.代码实现

package com.tx.prim;
import java.util.Arrays;
public class Prim {
	public static void main(String[] args) {
		char[] data = new char[]{'A','B','C','D','E','F','G'};
		int verxs = data.length;
		//邻接矩阵的关系使用二维数组表示,10000表示两个点不联通
		int [][]weight=new int[][]{
            {10000,5,7,10000,10000,10000,2},
            {5,10000,10000,9,10000,10000,3},
            {7,10000,10000,10000,8,10000,10000},
            {10000,9,10000,10000,10000,4,10000},
            {10000,10000,8,10000,10000,5,4},
            {10000,10000,10000,4,5,10000,6},
            {2,3,10000,10000,4,6,10000},};
            
        MGraph graph = new MGraph(verxs);
        MinTree minTree = new MinTree();
        
        minTree.createGraph(graph, verxs, data, weight);
        minTree.showGraph(graph);
        minTree.primMethod(graph, 1);
	}

}

//创建最小生成树
class MinTree {
	public void createGraph(MGraph graph, int verxs, char data[], int[][] weight) {
		int i, j;
		for(i = 0; i < verxs; i++) {//顶点
			graph.data[i] = data[i];
			for(j = 0; j < verxs; j++) {
				graph.weight[i][j] = weight[i][j];
			}
		}
	}
	public void showGraph(MGraph graph) {
		for(int[] link: graph.weight) {
			System.out.println(Arrays.toString(link));
		}
	}
	public void primMethod(MGraph graph, int v) {
		int visited[] = new int[graph.verxs];
		visited[v] = 1;
		//h1 和 h2 记录下标
		int h1 = -1;
		int h2 = -1;
		int minWeight = 10000; 
		for(int k = 1; k < graph.verxs; k++) {
			for(int i = 0; i < graph.verxs; i++) {结点
				for(int j = 0; j< graph.verxs;j++) {
					if(visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) {
						minWeight = graph.weight[i][j];
						h1 = i;
						h2 = j;
					}
				}
			}
			//找到最小的一条边
			System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + "> 值:" + minWeight);
			//将当前这个结点标记为已经访问
			visited[h2] = 1;
			//minWeight 设置为最大值 10000
			minWeight = 10000;
		}
		
	}
}
class MGraph {
	int verxs; //表示图的节点个数
	char[] data;//存放结点数据
	int[][] weight; //存放边,就是我们的邻接矩阵
	
	public MGraph(int verxs) {
		this.verxs = verxs;
		data = new char[verxs];
		weight = new int[verxs][verxs];
	}
}


二、Kruskal算法

1.基本介绍

克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。
基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。
具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。

2.应用场景

prim算法和Kruskal算法

3.代码实现

代码如下(示例):

ipackage com.tx.kruskal;
import java.util.Arrays;
public class Kruskal {

	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[][] = {
	    
	 {   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}}; 
	      
	      Kruskal kruskalCase = new Kruskal(vertexs, matrix);
	     
	      kruskalCase.print();
	      kruskalCase.kruskalmethod();
	      
	}
	
	public Kruskal(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 kruskalmethod() {
		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);
		
		//遍历edges 数组
		for(int i=0; i < edgeNum; i++) {
			//获取到第i条边的第一个顶点
			int p1 = getPosition(edges[i].start); 
			//获取到第i条边的第2个顶点
			int p2 = getPosition(edges[i].end); 
			int m = getEnd(ends, p1); 
			//获取p2到终点
			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("邻接矩阵为: \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();
		}
	}
	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) { [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 + "]";
	}
	
	
}

上一篇:最小生成树


下一篇:Prim算法