求解城市之間的最短總距離是一個非常實際的問題,其大意如下:
某地區由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("\n最小生成樹的總權值為:%d\n", 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("輸入圖中各頂點信息\n"); for(i=0;i<GM.VertexNum;i++){ //輸入頂點 System.out.printf("第%d個頂點:", i+1); GM.Vertex[i]=(input.next().toCharArray())[0]; //保存到各頂點的數組元素中 } System.out.printf("輸入構成各邊的頂點及權值:\n"); 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("\t%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("\tZ"); //以Z表示無窮大 }else{ System.out.printf("\t%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("\n最小生成樹的總權值為:%d\n", 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("\n繼續玩嗎(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 游戲結束!