求解城市之间的最短总距离是一个非常实际的问题,其大意如下:
某地区由n个城市,如何选择一条路线使各个城市之间的总距离最短?
1.最短总距离算法
先来分析一下上述问题。某个地区的n个城市构成一个交通图,可以使用图结构来描述此问题,其对应关系如下:
- 每个城市代表图中的一个顶点。
- 两个顶点间的边即两个城市之间的路径,边的权值代表了城市间的距离。
这样,求解各个城市之间的最短总距离问题就归结为该图的最小生成树问题。
2.最小生成树算法(prim算法)
//最小生成树算法 static void PrimGraph(GraphMatrix GM){ int i,j,k,min,sum; int[] weight=new int[GraphMatrix.MaxNum]; //权值 char[] vtempx=new char[GraphMatrix.MaxNum]; //临时顶点信息 sum=0; for(i=1;i<GM.VertexNum;i++){ //保存邻接矩阵中的一行数据 weight[i]=GM.EdgeWeight[0][i]; if(weight[i]==MaxValue){ vtempx[i]=(char) NoL; }else{ vtempx[i]=GM.Vertex[0]; //邻接顶点 } } vtempx[0]=USED; //选用 weight[0]=MaxValue; for(i=1;i<GM.VertexNum;i++){ min=weight[0]; //最小权值 k=i; for(j=1;j<GM.VertexNum;j++){ if(weight[j]<min&&vtempx[j]>0){ //找到具有更小权值的未使用边 min=weight[j]; //保存权值 k=j; //保存邻接点序号 } } sum+=min; //权值累加 System.out.printf("(%c,%c),",vtempx[k],GM.Vertex[k]); //输出生成树一条边 vtempx[k]=USED; //选用 weight[k]=MaxValue; for(j=0;j<GM.VertexNum;j++){ //重新选择最小边 if(GM.EdgeWeight[k][j]<weight[j]&&vtempx[j]!=0){ weight[j]=GM.EdgeWeight[k][j]; //权值 vtempx[j]=GM.Vertex[k]; } } } System.out.printf(" 最小生成树的总权值为:%d ", sum); }
3.程序代码示例如下:
package com.cn.datastruct; import java.util.Scanner; //求解城市之间的最短总距离 public class Prim { //定义邻接矩阵图结构 static class GraphMatrix{ static final int MaxNum=20; //图的最大顶点数 char[] Vertex=new char[MaxNum]; //保存顶点信息(序号或字母) int GType; //图的类型(0:无向图,1:有向图) int VertexNum; //顶点的数量 int EdgeNum; //边的数量 int[][] EdgeWeight = new int[MaxNum][MaxNum]; //保存边的权 int[] isTrav = new int[MaxNum]; //遍历标志 } static final int MaxValue=65535; //最大值(可设为一个最大整数) static final int USED=0; //已选用顶点 static final int NoL=-1; //非邻接顶点 static Scanner input=new Scanner(System.in); //创建邻接矩阵图 static void CreateGraph(GraphMatrix GM){ int i,j,k; int weight; //权 char EstartV,EendV; //边的起始顶点 System.out.printf("输入图中各顶点信息 "); for(i=0;i<GM.VertexNum;i++){ //输入顶点 System.out.printf("第%d个顶点:", i+1); GM.Vertex[i]=(input.next().toCharArray())[0]; //保存到各顶点的数组元素中 } System.out.printf("输入构成各边的顶点及权值: "); for(k=0;k<GM.EdgeNum;k++){ //输入边的信息 System.out.printf("第%d条边:", k+1); EstartV=input.next().charAt(0); EendV=input.next().charAt(0); weight=input.nextInt(); for(i=0;EstartV!=GM.Vertex[i];i++); //在已有顶点中查找始点 for(j=0;EendV!=GM.Vertex[j];j++); //在已有的顶点中查找终点 GM.EdgeWeight[i][j]=weight; //对应位置保存权值,表示有一条边 if(GM.GType==0){ //若是无向图 GM.EdgeWeight[j][i]=weight; } } } //清空矩阵 static void ClearGraph(GraphMatrix GM){ int i,j; for(i=0;i<GM.VertexNum;i++){ for(j=0;j<GM.VertexNum;j++){ GM.EdgeWeight[i][j]=MaxValue; //设置矩阵中各元素的值为MaxValue } } } //输出邻接矩阵 static void OutGraph(GraphMatrix GM){ int i,j; for(j=0;j<GM.VertexNum;j++){ System.out.printf(" %c", GM.Vertex[j]); //在第一行输出顶点信息 } System.out.println(); for(i=0;i<GM.VertexNum;i++){ System.out.printf("%c", GM.Vertex[i]); for(j=0;j<GM.VertexNum;j++){ if(GM.EdgeWeight[i][j]==MaxValue){ //若权值为最大值 System.out.printf(" Z"); //以Z表示无穷大 }else{ System.out.printf(" %d", GM.EdgeWeight[i][j]); //输出边的权值 } } System.out.println(); } } //最小生成树算法 static void PrimGraph(GraphMatrix GM){ int i,j,k,min,sum; int[] weight=new int[GraphMatrix.MaxNum]; //权值 char[] vtempx=new char[GraphMatrix.MaxNum]; //临时顶点信息 sum=0; for(i=1;i<GM.VertexNum;i++){ //保存邻接矩阵中的一行数据 weight[i]=GM.EdgeWeight[0][i]; if(weight[i]==MaxValue){ vtempx[i]=(char) NoL; }else{ vtempx[i]=GM.Vertex[0]; //邻接顶点 } } vtempx[0]=USED; //选用 weight[0]=MaxValue; for(i=1;i<GM.VertexNum;i++){ min=weight[0]; //最小权值 k=i; for(j=1;j<GM.VertexNum;j++){ if(weight[j]<min&&vtempx[j]>0){ //找到具有更小权值的未使用边 min=weight[j]; //保存权值 k=j; //保存邻接点序号 } } sum+=min; //权值累加 System.out.printf("(%c,%c),",vtempx[k],GM.Vertex[k]); //输出生成树一条边 vtempx[k]=USED; //选用 weight[k]=MaxValue; for(j=0;j<GM.VertexNum;j++){ //重新选择最小边 if(GM.EdgeWeight[k][j]<weight[j]&&vtempx[j]!=0){ weight[j]=GM.EdgeWeight[k][j]; //权值 vtempx[j]=GM.Vertex[k]; } } } System.out.printf(" 最小生成树的总权值为:%d ", sum); } public static void main(String[] args) { GraphMatrix GM=new GraphMatrix(); //定义保存邻接表结构的图 char again; String go; System.out.println("寻找最小生成树!"); do{ System.out.print("请先输入生成图的类型:"); GM.GType=input.nextInt(); //图的种类 System.out.print("输入图的顶点数量:"); GM.VertexNum=input.nextInt(); //输入图的顶点数 System.out.print("输入图的边数量:"); GM.EdgeNum=input.nextInt(); //输入图边数 ClearGraph(GM); //清空图 CreateGraph(GM); //生成邻接表结构的图 System.out.print("最小生成树的边为:"); PrimGraph(GM); System.out.println(" 继续玩吗(y/n)?"); go=input.next(); }while(go.equalsIgnoreCase("y")); System.out.println("游戏结束!"); } }
程序运行结果如下:
寻找最小生成树! 请先输入生成图的类型:0 输入图的顶点数量:5 输入图的边数量:6 输入图中各顶点信息 第1个顶点:1 第2个顶点:2 第3个顶点:3 第4个顶点:4 第5个顶点:5 输入构成各边的顶点及权值: 第1条边:4 5 2 第2条边:3 5 5 第3条边:2 4 4 第4条边:1 5 3 第5条边:1 3 5 第6条边:1 2 2 最小生成树的边为:(1,2),(1,5),(5,4),(1,3), 最小生成树的总权值为:12 继续玩吗(y/n)? n 游戏结束!