最短路径算法Dijkstra思路及java实现

2 篇文章 0 订阅
2 篇文章 0 订阅
    最近在问答上看到了一个求取最短路径的问题,曾经也了解过,但从来没有实现出来。正好有空就好好研究下呗,能力有限参考了各位大佬的文章,我就记录记录我的思考和实现过程吧。

Dijkstra算法

节点路径图示例
  算法的思路,有很多篇文章都讲过了,我就不再累赘。我用简短的话语重述一下步骤吧:
  路径图需要满足一定的条件及进行预处理。有两个集合:S和U,S中为已求得最短距离节点,U中为待求得的最短距离节点。
  1.两两相邻节点间的距离为正,如0—>1: Δ3,不相邻节点间距离为∞或其他标志符,如0—>4: ∞。
  2.从第一个起始节点开始,遍历到其它节点的距离且排除S集合中的节点(0—>1,0—>2,0—>3,,,),得到其中距离最短的节点。
  3.将2步中求得的节点加入集合S中,并以此节点为新的起始节点。
  4.重复2、3步,直到所有节点求取完毕。


1、具体思想

个人理解:
  1.将各相邻节点距离规范化,尤其是不相邻节点距离标记为∞,这样能够很好的用于计算机处理。
  2.为何在当前遍历的节点距离集中,选择最小的值及其对应的节点作为最终的短路径?
  因为此节点必为最短路径值。例如以0节点为起始点:

路径距离值
0—>13
0—>22
0—>3
0—>4
0—>5

  可见0—>2路径值最小,但可能还有其他路径0—>1—>2或者0—>3—>4—>2等通过其他节点间接到达2节点。
  但是,0—>3和0—>4等不相邻已排除,0—>1—>n—>2的距离等于”0—>1”+”1—>n”+”n—>2”,首先0—>1的距离值就已经大于0—>2的值,所以不存在其它最短路径到2节点,依次类推。
  3.为何选取2步中得出的最短节点作为下一次的起点?
排除2步中求得节点2之后,当前路径值如下所示:

路径距离值
0—>13
0—>3
0—>4
0—>5

  但我们不确定0—>1是否是最短路径值,只有比它距离短的一条“0—>2”路径+另一条路径才可能出现更短的路径,即“2+?<3”。
  所以选择上步得到的最短路径来排除剩余的路径是否为最短路径。


2、实施步骤
    仅靠语言描述还不太清楚,具体的细节看如下步骤吧。
路径距离值
0—>13
0—>22
0—>3
0—>4
0—>5

选出2节点,排除并以2节点为新的起始点遍历其余点。

路径距离值合并
0—>132+0.5
0—>32+5
0—>4
0—>5
0—>2—>12+0.5
0—>2—>32+5
0—>2—>42+∞
0—>2—>52+∞

选出1节点,排除并以1节点为新的起始点遍历其余点。

路径距离值合并
0—>32+5
0—>42.5+3.5
0—>5
0—>2—>32+5
0—>2—>42+∞
0—>2—>52+∞
0—>2—>1—>32.5+4.5
0—>2—>1—>42.5+3.5
0—>2—>1—>52.5+∞

选出4节点,排除并以4节点为新的起始点遍历其余点。

路径距离值合并
0—>32+5
0—>56+1
0—>2—>32+5
0—>2—>52+∞
0—>2—>1—>32.5+4.5
0—>2—>1—>52.5+∞
0—>2—>1—>4—>36+3
0—>2—>1—>4—>56+1

0—>2—>1—>3和0—>2—>1—>4—>5距离值相同,任取其一,排除并重新遍历剩余点。

路径距离值合并
0—>56+1
0—>2—>52+∞
0—>2—>1—>52.5+∞
0—>2—>1—>4—>56+1
0—>2—>1—>3—>57+∞

最后选出节点5,得到最短距离为7,循环结束。


3、总结规律

根据最短路径选取的步骤,总结化简,得出以下规律:

规律
1.无论中间经过多少个节点,最终都是求初始顶点到其余点的距离,所以可以合并各路径值,如上列表中的合并栏,只取当前最小值替换。
2.合并后的序列组,然后只需比较合并后的数组得出的最小值,即为所有序列的路径的最小值。
3.每循环一次,都可得出一组节点的最短距离,所以最多循环n次可求出所有节点的最短距离。

已知条件和待求问题为:

已知条件待求值
两两节点间的距离初始顶点到每个节点的最小值
每步选取的节点号
排除的节点标记

总结规律,化简各种条件便于编写程序。


4、Java程序实现
import java.util.Scanner;;
public class ShortestPath 
{
    private double[][] distance;  //两两节点间的距离矩阵
    private double[] path;        //起点到各节点间的最短距离值
    private boolean[] flag;       //已选出的节点标记
    private String[]route;        //最短路径规划方案

    public static void main(String[] argument)
    {
        ShortestPath test=new ShortestPath();
        test.init(6);
        test.Calculate(1);
        test.showDistance();
    }
    /**
     * 初始化两两间的路径
     * @param count 总节点数
     * 不相邻的节点距离设为-1
     */
    public void init(int count) 
    {
        distance=new double[count][count];
        path=new double[count];
        flag=new boolean[count];
        route=new String[count];
        Scanner keyboard=new Scanner(System.in);
        for(int i=0;i<count;i++)
        {
            for(int j=i+1;j<count;j++)
            {
                System.out.println("Please enter the distance "+i+"--->"+j+":");
                distance[i][j]=keyboard.nextDouble();
                distance[j][i]=distance[i][j];
            }
            distance[i][i]=0;
            path[i]=Double.MAX_VALUE;
            flag[i]=false;
        }
    }
    /**
     * 显示各节点间的距离、初始顶点到各节点的最短路径方案和距离值
     */
    public void showDistance()
    {
        for(int i=0;i<distance.length;i++)
        {
            for(int j=0;j<distance.length;j++)
            {
                System.out.println(i+"--->"+j+": "+distance[i][j]);
            }
        }
        System.out.println("The shortest path:");
        for(int j=0;j<distance.length;j++)
        {
            System.out.println("--->"+j+": "+path[j]+"  "+route[j]);
        }
    }
    /**
     * 运算出起点到每个节点的最短距离
     * @param startNode   起始顶点
     */
    public void Calculate(int startNode)
    {
        //第一次初始化写入Path时使distance+min<path成立
        double min=0;                     
        String shortRoute=String.valueOf(startNode);
        int node=startNode;
        //每循环一次找出一个节点,最多循环n次
        for(int i=0;i<distance.length;i++)
        {
            //合并成01,02,03,,,的n列数组
            for(int j=0;j<distance.length;j++)               
            {
                //已经选出的最短节点和不相邻节点(-1)不参与
                if(distance[startNode][j]>=0&&flag[j]==false)
                {
                    //当有更短的路径方案时,替换路径值和方案
                    if(distance[startNode][j]+min<path[j])   
                    {
                        path[j]=distance[startNode][j]+min;
                        route[j]=shortRoute+"--->"+j;
                    }
                }
            }
            //找出数组中最短的值,提取出该节点序列号和距离值
            min=Double.MAX_VALUE;
            for(int j=0;j<distance.length;j++)
            {
                if(flag[j]==false)
                {
                    if(path[j]<min)
                    {
                        min=path[j];
                        node=j;
                    }   
                }
            }
            flag[node]=true;
            startNode=node;
            shortRoute=route[node];
        }
    }
}

5、测试效果

输入两两节点路径矩阵
  为了便于判断,我设定两两不相邻节点间距离为负数,暂定为“-1”,并且0—>3的值等于3—>0的值。
运行结果
  修改定义的起始顶点,测试不同结果。

public static void main(String[] argument)
    {
        ShortestPath test=new ShortestPath();
        test.init(6);
        //将初始顶点设为3,计算3到其余节点的最短路径
        test.Calculate(3);
        test.showDistance();
    }

起始顶点为3运行结果

6、总结

  在编写程序中,当然还有些小细节和技巧。例如最后的最优路径规划图3—>2—>0的得出(总结规律,是在上次最优路径基础上加上—>当前节点,且”上次最优值+当前节点值<历史最优path”才进行替换操作)。
  当然不可否认,当前程序还不够精炼,处理效率还是低了点,并且还有其它更好的寻径算法。偶尔研究下算法进行脑力训练也是不错的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值