图结构求解城市间最短路径问题java实现

通过Graph结构求解城市间最短路径

问题描述: 已知某地区的多个城市与部分城市之间的最短距离,求给定的两个城市之间的最短路径。
  例如:已知城市A、B、C、D、E、F六座城市,城市之间的最短距离情况如下(可以为有向或者无向,这里以无向为例):
A——B:2km
A——C:9km
A——E:3km
B——D:3km
C——E:5km
C——D:2km (F城作为边界条件,故上述无到达F城的路径,实际使用中可以不予考虑)
要求求出A城到其他各城的最短路径,或A城到指定城市(如D城)的最短路径。

解决思路: 上述问题属于典型的求顶点间最优路径的问题,借助图论进行求解。将各城市作为图的顶点,城市间的路径作为图的边,路径长度为对应边的权重构造图,之后采用Dijkstra算法求解最优路径。本文用java实现的代码,是在徐明远等编所著《Java常用算法手册》的示例代码【程序7-5】的基础上优化所得,封装了一部分属性使代码易用已读。代码如下:

首先新建Graph类文件以存储数据

/**
 * 新建Graph类,用于存储数据
 * @author Misui_user
 *
 */
class GraphMatrix 
{   
    public static final int  MaxValue=65535;        //最大值(可设为一个最大整数)
    char[] Vertex;                                  //保存顶点信息(序号或字母)
    int GType=0;                                    //图的类型(0:无向图,1:有向图)
    int VertexNum;                                  //顶点的数量 
    int EdgeNum;                                    //边的数量 
    int[][] EdgeWeight;                             //保存边的权 
    /**
     * 设置顶点
     * @param points
     */
    public void setVertex(char[] points){
        this.Vertex=new char[points.length];
        this.VertexNum=points.length;
        for(int i=0;i<this.VertexNum;i++)           //输入顶点 
        {           
            this.Vertex[i]=points[i];               //保存到各顶点数组元素中 
        }
    }
    /**
     * 设置边及其权重
     * @param roads
     * @param weights
     */
    public void setEdgeWeight(char[][] roads,int[] weights){
        this.EdgeWeight=new int[roads.length][roads.length];
        ClearGraph();
        this.EdgeNum=roads.length;
        for(int k=0;k<this.EdgeNum;k++)         //输入边的信息 
        {
            int i=indexOf(roads[k][0]);     //在已有顶点中查找始点位置 
            int j=indexOf(roads[k][1]);     //在已有顶点中查找结终点 位置
            this.EdgeWeight[i][j]=weights[k];       //对应位置保存权值,表示有一条边
            if(this.GType==0)               //若是无向图
            {
                this.EdgeWeight[j][i]=weights[k];   //在对角位置保存权值  
            }
        }       
    }
    /**
     * 清空邻接矩阵
     * @param GM
     */
    private void ClearGraph()
    {
        for(int i=0;i<this.VertexNum;i++)       //清空矩阵 
        {
            for(int j=0;j<this.VertexNum;j++)
            {
                this.EdgeWeight[i][j]=MaxValue; //设置矩阵中各元素的值为MaxValue
            }
        }
    }
    public int indexOf(char c){
        for(int i=0;i<VertexNum;i++){
            if(c==Vertex[i]){
                return i;
            }
        }
        return -1;
    }
}   

新建操作类,实现Dijkstra算法

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class DisMinGraph {
    static final int  MaxValue=65535;               //最大值(可设为一个最大整数)
    static Scanner input=new Scanner(System.in);

    public static void main(String[] args) {
        GraphMatrix GM=new GraphMatrix();       //定义保存邻接表结构的图 
        System.out.printf("求解最短路径问题!\n");
        int VertexNum=input.nextInt();          //输入图顶点数 
        int EdgeNum=input.nextInt();            //输入图边数 
        char[] points=new char[VertexNum];      //读取顶点信息
        char[][] roads=new char[EdgeNum][2];    //读取边信息,起点和终点
        int[] weight=new int[EdgeNum];          //读取边权重
        for(int i=0;i<points.length;i++){
            points[i]=(input.next().toCharArray())[0];
        }
        for(int i=0;i<roads.length;i++){
            roads[i][0]=(input.next().toCharArray())[0];
            roads[i][1]=(input.next().toCharArray())[0];
            weight[i]=input.nextInt();
        }
        CreateGraph(GM,points,roads,weight);    //生成邻接表结构的图
//      OutGraph(GM);                           //输出邻接矩阵
        int vend=GM.indexOf((input.next().toCharArray())[0]);   //获取出发点位置
        if(vend==-1){
            System.out.println("顶点不存在,请重新输入!");
            return;
        }
        int[][] disMin=DistMin(GM,vend);        //执行最小路径算法
        System.out.println("各顶点到达顶点“"+GM.Vertex[vend]+"”的最短路径分别为(起始点 - 结束点):");
        for(int i=0;i<GM.VertexNum;i++)         //各个顶点到出发点的路径
        {
            char[][] path=getPath(GM,disMin,vend,i);
            System.out.println("-->"+Arrays.toString(path[0])+"-------------->路径长度:"+(int)path[1][0]);
        }

    }

    private static void CreateGraph(GraphMatrix GM,char[] points,char[][] roads,int[] weights){     //创建邻接矩阵图
        GM.setVertex(points);
        GM.setEdgeWeight(roads, weights);
    }
    /**
     * 输出图的邻接矩阵
     * @param GM
     */
    public static void OutGraph(GraphMatrix GM)
    {
        int i,j;
        System.out.print("\n");
        for(i=0;i<GM.VertexNum;i++) 
        {
            for(j=0;j<GM.VertexNum;j++)
            {
                if(GM.EdgeWeight[i][j]==MaxValue) //若权值为最大值 
                {
                    System.out.print("\tZ");                //以Z表示无穷大
                }
                else
                {
                    System.out.print("\t"+GM.EdgeWeight[i][j]); //输出边的权值
                }
            }
            System.out.print("\n");
        }             
    }
    /**
     * 通过遍历图,生成可到达指定顶点的顶点数组tmpvertex,以及各顶点到达指定定点路径缓存数组path,实现Dijkstra算法
     * @param GM
     * @param vend
     * @return 数组tmpvertex与数组path,联合表示指定顶点(出发点)到达各顶点的最小路径
     */
    private static int[][] DistMin(GraphMatrix GM,int vend)         //最短路径算法
    {
        //可到达指定顶点(出发点)的顶点集合,下标表示顶点位置,值表示是否可以到达,0表示不能,1表示能
        int[] tmpvertex=new int[GM.VertexNum];
        //从所有顶点到达指定顶点(出发点)的路径索引数组;下标为对应顶点,值为对应顶点可到达的下一个顶点
        int[] path=new int[GM.VertexNum];
        int[] weight=new int[GM.VertexNum];         //某终止点到各顶点的最短路径长度
        for(int i=0;i<GM.VertexNum;i++)             //初始weight数组 
        {
            tmpvertex[i]=0;                     //初始化顶点集合为空 
            weight[i]=GM.EdgeWeight[vend][i];   //保存最小权值 
            if(weight[i]<MaxValue && weight[i]>0) //有效权值 
            {
                path[i]=vend;               //保存边
            }
        }
        tmpvertex[vend]=1;                  //选入顶点vend
        weight[vend]=0;  
        for(int i=0;i<GM.VertexNum;i++)
        {
            int min=MaxValue; 
            int k=vend; 
            for(int j=0;j<GM.VertexNum;j++)         //查找未用顶点的最小权值 
            {
                if(tmpvertex[j]==0 && weight[j]<min)
                {
                    min=weight[j];
                    k=j;
                }
            }
            tmpvertex[k]=1;                     //将顶点k选为可到达指定定点(出发点)
            for(int j=0;j<GM.VertexNum;j++)         //以顶点k为中间点,重新计算权值 ,判断是否有以顶点k为中继到达指定定点(出发点)权值更小的点
            {
                if(tmpvertex[j]==0 && weight[k]+GM.EdgeWeight[k][j]<weight[j])  
                {
                    weight[j]=weight[k]+GM.EdgeWeight[k][j]; 
                    path[j]=k;
                }
            }
        }
        return new int[][]{tmpvertex,path};
    }
    /**
     * 输出指定顶点(出发点)到特定顶点end的路径和该路径的长度
     * @param GM 图对象
     * @param disMin 经DistMin()方法计算后得到的最小路径表示结果
     * @param vend 出发点的位置
     * @param end 结束点的位置
     * @return 返回结果为路径与该路径的长度,result[0]为路径,result[1][0]为长度
     */
    public static char[][] getPath(GraphMatrix GM,int[][] disMin,int vend,int end){
        ArrayList<Character> path=new ArrayList<Character>();
        int l=0;
        if(disMin[0][end]==1)
        {
            int k=end;            
            while(k!=vend)
            {
                path.add(GM.Vertex[k]);
                l+=GM.EdgeWeight[k][disMin[1][k]];
                k=disMin[1][k];
            }
            path.add(GM.Vertex[k]);
        }
        char[] p=new char[path.size()];
        for(int i=0;i<p.length;i++){
            p[i]=path.get(i);
        }
        return new char[][]{p,{(char)l}};
    }

}

测试用例

//输入顶点数量
6
//输入边数量
7
//输入顶点
A
B
C
D
E
F
//输入边及其权重
A B 2
A C 9
A E 3
B D 3
C E 5
D E 2
C D 2
//输入出发点名称
A

测试结果:

各顶点到达顶点“A”的最短路径分别为(起始点 - 结束点):
–>[A]————–>路径长度:0
–>[B, A]————–>路径长度:2
–>[C, D, B, A]————–>路径长度:7
–>[D, B, A]————–>路径长度:5
–>[E, A]————–>路径长度:3
–>[]————–>路径长度:0

  用例的测试结果输出了A城到所有其他各城的最优路径,实际上是多次调用了DisMinGraph.getPath(GraphMatrix GM,int[][] disMin,int vend,int end)方法将所有城市到A城的路径分别进行了计算,如果需要返回指定城市(A城、D城)之间的距离,可在调用DisMinGraph.DistMin(GraphMatrix GM,int vend)方法对指定顶点使用Dijkstra算法后(这里的(指定顶点)必须是A或者D),再调用getPath方法即可获得两城之间的最优路径信息。
  上述代码,除可以解决无向图的顶点间最有路径问题外,还可以解决有向图的最优路径问题,切换GraphMatrix.GType属性即可。


  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值