程序员必须会的基本算法6-Prim算法(普利姆算法)

最小生成树问题

普里姆算法要解决的就是最小生成树问题(MinimumCostSpanningTree),,简称MST。
给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树
最小生成树的特点:
1)N个顶点就一定有N-1条边
2)包含全部的顶点

普利姆算法

普利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,
找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图

问题例子

现在有7个村庄(A,B,C,D,E,F,G),现在需要修路把7个村庄连通
各个村庄的距离用边线表示(权),比如A–B距离5公里
如何修路保证各个村庄都能连通,并且总的修建公路总里程最短?
在这里插入图片描述

解决

prim算法是同图的随便一个节点进行开始,无论从哪一个节点开始,最终生成的最小生成树的权重都是一样的,那么现在就从A开始进行处理
第一步
首先标记A节点已经被访问过 visited=[A]
然后看和A相连结的还没有被访问过的节点,那么现在就有
A-C(7),A-G(2),A-B(5),其中2是最小的,就连接A-G,
然后标记G已经被访问过,visited=[A,G]
第二步
现在A和G已经被访问过了,将AG看作一个整体,也是找和AG相连的还没被访问过的节点,那么现在有
A-C(7),G-E(4),G-F(6),G-B(3),A-B(5),其中3是最小的,连接G-B
然后标记B已经被访问过,visited=[A,G,B]
然后
一直这样循环下去,直到找出了n-1条边,就是找出6条边

代码实现

package basic;

import java.util.Arrays;

public class Prim
{
    
    
	public static 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},};
    
    public static char[] nodes=new char[] {
    
    'A','B','C','D','E','F','G'};
    public static int number=nodes.length;
    public static Graph graph=new Graph(number);
    
	public static void main(String[] args)
	{
    
    
		CreateGraph(graph, number, nodes, weight);
		show(graph);
		prim(graph, 0);
	}
	/*
	 * 普利姆算法的核心,传入的参数分别是图graph和从哪一个节点开始index
	 * 首先创建一个标记节点是否被访问过的数组visited
	 * 然后将传进来的index节点标记为已经被访问过,还要定义一个数值很大的MinWeight
	 * 用来寻找图中小的权值的边
	 * 然后是for循环,我们需要生成graph.number-1条边
	 * 所以第一重for循环只是循环需要生成的边
	 * 然后再是两层的for循环,用来遍历整个图的,寻找最小的还没访问的边
	 */
	public static void prim(Graph graph,int index)
	{
    
    
		boolean[] visited=new boolean[graph.number];
		visited[index]=true;
		int MinWeight=Integer.MAX_VALUE;
		int x1=0,x2=0;
		
		for(int x=0;x<graph.number-1;x++)
		{
    
    
			for(int i=0;i<graph.number;i++)
			{
    
    
				for(int j=0;j<graph.number;j++)
				{
    
    
					if(visited[i]&&!visited[j]&&graph.weight[i][j]<MinWeight)
					{
    
    
						MinWeight=graph.weight[i][j];
						x1=i;
						x2=j;
					}
				}
			}
			System.out.println("边:" + graph.nodes[x1] + "-" + graph.nodes[x2] + ",权值:" + MinWeight);
			visited[x2]=true;
			MinWeight=Integer.MAX_VALUE;
		}
	}
	
	/**
	 * 这是初始化对应的图对象,创建好对应的邻接矩阵
	 * @param graph 图对象
	 * @param number 节点数
	 * @param nodes  保存节点的数组
	 * @param weight  保存权重的邻接矩阵
	 */
	public static void CreateGraph(Graph graph,int number,char[] nodes,int[][] weight)
	{
    
    
		for(int x=0;x<number;x++)
		{
    
    
			graph.nodes[x]=nodes[x];
			for(int y=0;y<number;y++)
			{
    
    
				graph.weight[x][y]=weight[x][y];
			}
		}
	}
	public static void show(Graph graph)
	{
    
    
		for(int[] temp:graph.weight)
		{
    
    
			System.out.println(Arrays.toString(temp));
		}
	}
}
/*
 * 首先创建图对象,包含图的节点个数的number
 * 保存每一个节点的数组nodes,保存边的权重的邻接矩阵weight
 */
class Graph
{
    
    
	int number;
	char[] nodes;
	int[][] weight;
	public Graph(int number)
	{
    
    
		this.number = number;
		this.nodes=new char[number];
		this.weight=new int[number][number];
	}
	
}

猜你喜欢

转载自blog.csdn.net/qq_43416157/article/details/108740222
今日推荐