无向图中最小生成树的解决办法(1) ----- Prim算法

什么是最小生成树?

对于n个顶点构成的通信网,通信网可以构建许多不同的生成树,每一颗生成树都是一个通信网。我们现在要在该通信网中找到一颗生成树,该生成树连接所有顶点,且总花费最小。这样的生成树即称为最小生成树

prim算法的求解过程

对于如下无向图,试求其最小生成树,顶点和顶点之间边的值为两点之间的花费。
无向图中最小生成树的解决办法(1) ----- Prim算法_第1张图片

prim算法求解思想
我们假设N=(V,{E})是连通网络,TE是N上最小生成树的边的集合。算法从 U={u0} (u0∈V,选取某个点作为起始点,该点即为u0),TE={ }开始,重复下述操作:在所有 u∈U,v∈V - U 的边 (v,u) ∈ E中找到一条最小的边(v0,u0)加入到集合TE中,同时将v0并入U,直到U = V为止。下面我们选取v0作为起始点来具体描述这个过程。特别要注意的是在添加边的时候,该边的两个顶点一定各自属于U和V-U
无向图中最小生成树的解决办法(1) ----- Prim算法_第2张图片

代码实现


import java.util.Scanner;
/*
prim算法
测试数据:
6
10
0
0 1 6
0 3 5
0 2 1
1 2 5
2 3 5
1 4 3
3 5 2
2 4 6
2 5 4
4 5 6
*/

public class Mian{
   public static void main(String[] args){
       Scanner sc = new Scanner(System.in);
       int tn = sc.nextInt();   //输入无向图顶点的个数
       int te = sc.nextInt();   //输入无向图边的条数
       int sn = sc.nextInt();   //输入起始点
         
       int[][] inputGraph = new int[te][3];  //录入无向图,按照  "顶点1  顶点2  花费"来录入数据
       
       for(int i=0;i<te;i++){
          inputGraph[i][0] = sc.nextInt();
          inputGraph[i][1] = sc.nextInt();
          inputGraph[i][2] = sc.nextInt();
       }
       sc.close();
   
       //将其转化为真正的无向图graph
       int[][] graph = new int[tn][tn];
       int endless = 100;   //表示无穷远,初始化无向图自己到自己的距离为0,,到其它点的距离为无穷远
       for(int i=0;i<tn;i++){
          for(int j=0;j<tn;j++){
              graph[i][j] = endless;
              graph[i][i] = 0;
          }
       }
       //录入实际的数据
       for(int i=0;i<te;i++){
            graph[inputGraph[i][0]][inputGraph[i][1]] = inputGraph[i][2];
            graph[inputGraph[i][1]][inputGraph[i][0]] = inputGraph[i][2];
       }
   
       prim(graph,sn,tn);
   }
   
   //prim算法求最小生成树
   public static void prim(int[][] graph,int startNode,int NodeTotalNums){ //参数为无向图,起始点和结点的个数
       int[] distance = new int[NodeTotalNums];    //distance数组用来记录集合U到集合V-U的各个顶点的最短距离
       int[] closest = new int[NodeTotalNums];     //closest数组用来记录当前集合V-U的各点到集合U最近的点

       //初始化distance数组和closeest数组
       for(int i=0;i<NodeTotalNums;i++){
           distance[i] = graph[startNode][i];
           closest[i] = startNode;
       }

       //开始寻找最小生成树,连接NodeNums个结点至少需要NodeNums-1条边,所以外循环NodeNums-1次
       int sum=0,nextArriveNode=startNode;   //sum用来最小生成树的花费,nextArriveNode表示即将到达的顶点
       for(int i=0;i<NodeTotalNums-1;i++){
           int min = 100;  //初始假设集合U到集合V-U的无穷远,即没有通路,在这里我们用距离100表示无穷远
           for(int j=0;j<NodeTotalNums;j++){
               if(distance[j]<min && distance[j] != 0){
                   min = distance[j];
                   nextArriveNode = j;
               }
           }
           System.out.println("第"+(i+1)+"步: "+closest[nextArriveNode]+"->"+nextArriveNode);
           sum += distance[nextArriveNode];

           //将该结点加入集合U
           distance[nextArriveNode] = 0; 
   
           //更新集合U到集合V-U的最短距离
           for(int k=0;k<NodeTotalNums;k++){
                 if(graph[nextArriveNode][k]<distance[k]){
                      distance[k] = graph[nextArriveNode][k];
                      closest[k] = nextArriveNode;
                 }
           }          
       }
       System.out.println("最小生成树的总花费: "+sum);
   }
}

算法总结

prim算法的时间复杂度为O(n^2),与网中的边数无关,因此适用于求边稠密的网的最小生成树。prim算法利用了最小生成树的MST性质:假设N={V,{E}}是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中 u∈U,v∈V-U,则必存在一颗包含边(v,u)的最小生成树。

你可能感兴趣的:(算法和数据结构)