java 最短路径

两种算法

1.Dijkstra算法

package Tu;

import java.util.ArrayList;
import java.util.List;

public class MinTree {
    static int MAX_VALUE = 65535;
    public static void dir(int[][] matrix, int size){
        int[] shortPath = new int[size];
        boolean[] isgetPath = new boolean[size];
        int minPath;
        int minVertex = 0;// 找到的最短权值的顶点
        int sum = 0;
        // 初始化第一行顶点权值路径
        for (int i = 0; i < size; i++) {
            shortPath[i] = matrix[0][i];
        }
        isgetPath[0] = true;
        System.out.println("从第0个顶点开始查找");
        for (int i = 1; i < size; i++) {
            minPath = MAX_VALUE;
        // 找到已知权值数组中最小的权值
            for (int j = 1; j < size; j++) {
                if (!isgetPath[j] && shortPath[j] < minPath) {
                    minPath = shortPath[j];
                    minVertex = j;
                }
            }
            sum+= minPath;
            System.out.println("找到最短路径顶点:" + minVertex+" 权值为:"+minPath);
            isgetPath[minVertex] = true;
            for (int j = 1; j < size; j++) {
        // 根据找到的最端路径的顶点去遍历添加,最新路径
                if (!isgetPath[j] && (minPath + matrix[minVertex][j]) < shortPath[j]) {
                    shortPath[j] = minPath + matrix[minVertex][j];
                }
            }
        }
        System.out.println("---------最短路径为:"+sum);
        for (int j = 1; j < size; j++) {
            System.out.println("找到V0到顶点V" + j + "的最短路径:"+shortPath[j]);
        }


    }
    public static void main(String args[]){

        int[][] matrix = new int[9][9];
        int[] a0 = { 0, 1, 5, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE };
        int[] a1 = { 1, 0, 3, 7, 5, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE };
        int[] a2 = { 5, 3, 0, MAX_VALUE, 1, 7, MAX_VALUE, MAX_VALUE, MAX_VALUE };
        int[] a3 = { MAX_VALUE, 7, MAX_VALUE, 0, 2, MAX_VALUE, 3, MAX_VALUE, MAX_VALUE };
        int[] a4 = { MAX_VALUE, 5, 1, 2, 0, 3, 6, 9, MAX_VALUE };
        int[] a5 = { MAX_VALUE, MAX_VALUE, 7, MAX_VALUE, 3, 0, MAX_VALUE, 5, MAX_VALUE };
        int[] a6 = { MAX_VALUE, MAX_VALUE, MAX_VALUE, 3, 6, MAX_VALUE, 0, 2, 7 };
        int[] a7 = { MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, 9, 5, 2, 0, 4 };
        int[] a8 = { MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, MAX_VALUE, 7, 4, 0 };
        matrix[0] = a0;
        matrix[1] = a1;
        matrix[2] = a2;
        matrix[3] = a3;
        matrix[4] = a4;
        matrix[5] = a5;
        matrix[6] = a6;
        matrix[7] = a7;
        matrix[8] = a8;
        dir(matrix,matrix.length);
    }

}

该算法与prim算法相似,不同在于

prim算法更新路径时比较lowcost[j]与gaph[mid][j]的值

该算法比较shortpath[j]与min+matrix[mid][j]的值

即prim算法每次添加的是离树最近的非树顶点,迪杰斯特拉添加的是离起点最近的非树顶点


2.弗洛伊德算法

081028xjgvimgz7882qdu7.png

上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径。这个问题这也被称为“多源最短路径”问题。

现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储。比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为∞。另外此处约定一个城市自己是到自己的也是0,例如e[1][1]为0,具体如下。

081028o2n5ebn8hdeh9e5l.png

现在回到问题:如何求任意两点之间最短路径呢?通过之前的学习我们知道通过深度或广度优先搜索可以求出两点之间的最短路径。所以进行n2遍深度或广度优先搜索,即对每两个点都进行一次深度或广度优先搜索,便可以求得任意两点之间的最短路径。可是还有没有别的方法呢?

我们来想一想,根据我们以往的经验,如果要让任意两点(例如从顶点a点到顶点b)之间的路程变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不只通过一个点,而是经过两个点或者更多点中转会更短,即a->k1->k2b->或者a->k1->k2…->k->i…->b。比如上图中从4号城市到3号城市(4->3)的路程e[4][3]原本是12。如果只通过1号城市中转(4->1->3),路程将缩短为11(e[4][1]+e[1][3]=5+6=11)。其实1号城市到3号城市也可以通过2号城市中转,使得1号到3号城市的路程缩短为5(e[1][2]+e[2][3]=2+3=5)。所以如果同时经过1号和2号两个城市中转的话,从4号城市到3号城市的路程会进一步缩短为10。通过这个的例子,我们发现每个顶点都有可能使得另外两个顶点之间的路程变短。好,下面我们将这个问题一般化。

当任意两点之间不允许经过第三个点时,这些城市之间最短路程就是初始路程,如下。

081029zdxxq919ttqt8tu8.png

如现在只允许经过1号顶点,求任意两点之间的最短路程,应该如何求呢?只需判断e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。其中i是1~n循环,j也是1~n循环,代码实现如下。

 
  1. for(i=1;i<=n;i++)   
  2. {   
  3.     for(j=1;j<=n;j++)   
  4.     {   
  5.  if ( e[i][j] > e[i][1]+e[1][j] )   
  6. e[i][j] = e[i][1]+e[1][j];   
  7.     }   

在只允许经过1号顶点的情况下,任意两点之间的最短路程更新为:

081029itl7z7m4l9qqg56d.png

通过上图我们发现:在只通过1号顶点中转的情况下,3号顶点到2号顶点(e[3][2])、4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。

接下来继续求在只允许经过1和2号两个顶点的情况下任意两点之间的最短路程。如何做呢?我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为如下。

 
  1. //经过1号顶点   
  2. for(i=1;i<=n;i++)   
  3.     for(j=1;j<=n;j++)   
  4.  if (e[i][j] > e[i][1]+e[1][j])  e[i][j]=e[i][1]+e[1][j];   
  5. //经过2号顶点   
  6. for(i=1;i<=n;i++)   
  7.     for(j=1;j<=n;j++)   
  8.  if (e[i][j] > e[i][2]+e[2][j])  e[i][j]=e[i][2]+e[2][j]; 

在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:

081029e7gjlaaul4zk7z4n.png

通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。

同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

081029pd747o8o87o07o7l.png

最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:

081030h7tmht7cs2h7qftu.png

整个算法过程虽然说起来很麻烦,但是代码实现却非常简单,核心代码只有五行:

 
  1. for(k=1;k<=n;k++)   
  2.     for(i=1;i<=n;i++)   
  3.  for(j=1;j<=n;j++)   
  4.      if(e[i][j]>e[i][k]+e[k][j])   
  5.    e[i][j]=e[i][k]+e[k][j]; 

这段代码的基本思想就是:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。其实这是一种“动态规划”的思想,关于这个思想我们将在《啊哈!算法2——伟大思维闪耀时》在做详细的讨论。下面给出这个算法的核心代码:

/Floyd-Warshall算法核心语句   
for(k=1;k<=n;k++)   
  for(i=1;i<=n;i++)   
      for(j=1;j<=n;j++)   
 if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j])   
     e[i][j]=e[i][k]+e[k][j]; 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,PTA(Priority-Driven A*)算法是一种启发式搜索算法,用于寻找从起点到终点的最路径。它结合了Dijkstra算法和A*算法的特点,其中Dijkstra用于解决无权图的问题,而A*则使用了一个估价函数来优先探索更接近目标节点的路径。 以下是PTA Java中最路径的基本步骤: 1. 初始化:创建一个开放列表(Open List),一个关闭列表(Closed List),起点为初始状态,终点为终止状态。设置起点的f值(f = g + h)为0,其中g值为实际代价,h值为启发式代价(通常是曼哈顿距离或欧几里得距离)。 2. 建立数据结构:通常使用优先队列(如PriorityQueue)来存储节点,按照f值排序。 3. 查找路径:循环直到开放列表为空或找到终点: a. 从优先队列中取出f值最小的节点(当前节点)。 b. 如果当前节点是终点,则回溯构建路径。 c. 否则,对于当前节点的所有邻居,检查它们是否在关闭列表中,如果不在,则加入开放列表,并更新它们的g值、f值和父节点引用。 4. 更新状态:将当前节点标记为已访问并添加到关闭列表。然后计算每个邻居的g值(从起点到该节点的实际代价加上从当前节点到邻居的代价)和f值(g值加上从起点到终点的启发式估计),根据这些值调整优先级。 5. 如果没有找到路径:说明从起点到终点可能不存在路径,或者图中存在负权边,导致传统Dijkstra算法失效。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值