最短路径算法

最短路径


​ 从图的一个点到另一个点到路径不止一条,每条路径的长度可能不同,把路径长度最短的那条叫做最短路径。

有权图中,应该考虑各边的权值。无权图中,可以将每条边的权值看作是1.

​ 最短路径问题可分为两方面:

  • 图中一个点到其余各点的最短路径
  • 图中每对点之间到最短路径

Dijkstra

基本思想

​ 狄克斯特拉算法解决图中一点到其余各点到最短路径的问题。其基本思想位:图G=(V,E)是一个有权有向图,把顶点V分成两组,第一组为已求出最短路径的点的集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径v,…k,就将k加入到集合S中,直到全部到点都加入S集合中,算法结束),第二组为其余未确定最短路径的点的集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入到S中。

算法描述

操作步骤:

  1. 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为”起点s到该顶点的距离”[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
  2. 从U中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点k。
  3. 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
  4. 重复步骤(2)和(3),直到遍历完所有顶点。

图1在这里插入图片描述

如上图,以A为开头,求A到各个点的最短距离。

第一步,初始化距离,指与A直接连接的点的距离。dis[b]代表A到B点的最短距离,因而初始dis[B]=4,dis[C]=6,dis[D]=6,其余为无穷大。设置集合S用来表示已经找到的最短路径。此时,S={A}。现在得到A到各点距离**{A(0),B(4),C(6),D(6),E(*),F(*),G(*)}**,其中*代表未知数也可以说是无穷大,括号里面的数值代表A点到该点的最短距离。

第2步:不考虑集合S中的点,因为dis[B]=4,是当中距离最短的,所以此时更新S,S={A,B}。接着我们看与B连接的点,分别有C,E,已经在集合S中的不看**,dis[B-C]=1,因而dis[C]=dis[B]+1=5(小于之前点dis[C],故需更新),dis[E]=dis[B]+dis[B-E]=11**。此时**{A(0),B(4),C(5),D(6),E(11),F(*),G(*)}**。

第3步:在第2步中,C点的值5最小,更新S={A,B,C},此时看与C点直接连接的点,分别有E,F。dis[E]=dis[C]+dis[C-E]=5+6=11(和原来的值一样,不更新),dis[F]=dis[C]+dis[C-F]=5+4=9(更新)。此时**{A(0),B(4),C(5),D(6),E(11),F(9),G(*)}**。

第4步:在第3步中,D点的值6最小,更新S={A,B,C,D},此时看与D点直接连接的点,有F。dis[F]=dis[D]+dis[D-F]=6+5=11(不更新)。此时**{A(0),B(4),C(5),D(6),E(11),F(9),G(*)}**。

第5步:在第4步中,F点的值9最小,更新S={A,B,C,D,F},此时看与F点直接连接的点,有E、G。dis[E]=dis[F]+dis[F-E]=9+1=10(更新),dis[G]=dis[F]+dis[F-G]=9+8=17(更新)。此时**{A(0),B(4),C(5),D(6),E(10),F(9),G(17)}**。

第6步:在第5步中,E点的值10最小,更新S={A,B,C,D,F,E},此时看与E点直接连接的点,只有G。dis[G]=dis[E]+dis[E-G]=10+6=16(更新)。此时**{A(0),B(4),C(5),D(6),E(10),F(9),G(16)}**。

第7步:最后只剩下G值,直接进入集合S={A,B,C,D,F,E,G},此时所有的点都已经遍历结束,得到最终结果**{A(0),B(4),C(5),D(6),E(10),F(9),G(16)}**。

SUA点到个点到距离
{A}{B,C,D,F,E,G}{A(0),B(4),C(6),D(6),E(*),F(*),G(*)}
{A,B}{C,D,F,E,G}{A(0),B(4),C(5),D(6),E(11),F(*),G(*)}
{A,B,C}{D,F,E,G}{A(0),B(4),C(5),D(6),E(11),F(9),G(*)}
{A,B,C,D}{F,E,G}{A(0),B(4),C(5),D(6),E(11),F(9),G(*)}
{A,B,C,D,F}{E,G}{A(0),B(4),C(5),D(6),E(10),F(9),G(17)}
{A,B,C,D,F,E}{G}{A(0),B(4),C(6),D(6),E(10),F(9),G(16)}
{A,B,C,D,F,E,G}{}{A(0),B(4),C(6),D(6),E(10),F(9),G(16)}
public class Dijkstra {
    public static int max = 99999;

    public static void main(String[] args) {
        int[][] graph = {{0, 4, 6, 6, max, max, max},
                {max, 0, 1, max, 7, max, max},
                {max, max, 0, max, 6, 4, max},
                {max, max, 2, 0, max, 5, max},
                {max, max, max, max, 0, max, 6},
                {max, max, max, max, 1, 0, 8},
                {max, max, max, max, max, max, max}};
        int startPointIndex = 0;
        int[] dis = dijkastra(graph, startPointIndex);
        System.out.println(dis.toString());

    }

    public static int[] dijkastra(int[][] graph, int startPointIndex) {
        boolean[] s = new boolean[graph.length];//集合s表示,已经求出最短路径的点的集合
        for (int i = 0; i < graph.length; i++) {//初始化s集合,只有起始点
            if (i == startPointIndex) {
                s[i] = true;
            } else {
                s[i] = false;
            }
        }
        int[] dis = new int[graph.length];//最短路径集合
        for (int i = 0; i < graph.length; i++) {//初始化,起始点到其他点的距离。
            dis[i] = graph[startPointIndex][i];
        }

        for (int i = 0; i < graph.length; i++) {//求到每个点的最短路径

            int tmpdis = max;
            int tmpindex = 0;
            for (int j = 0; j < graph.length; j++) {
                if (!s[j] && dis[j] < tmpdis) {//不在s集合(未求出该店的最短路径)
                    tmpdis = dis[j];
                    tmpindex = j;
                }
            }
            //dis中距离最短的点加入到s集合中
            s[tmpindex] = true;
            
            for (int j = 0; j < graph.length; j++) {//更新起点到其他点距离
                if (dis[j] > dis[tmpindex] + graph[tmpindex][j]) {
                    dis[j] = dis[tmpindex] + graph[tmpindex][j];
                }
            }
        }
        return dis;//返回起点到每个点的最短路径长度
    }
}

狄克斯特拉算法的时间复杂度O(n^2)。

FLoyd算法

​ 对于第二类问题,求每对点之间的最短路径,可以通过把每个点作为源点用Dijkstra算法求最短路径。也可以用弗洛伊德(Floyd)算法。

基本思想

​ 有图G=(V,E)采用邻接矩阵cost存储,另外设置一个二维数组A用来存放当前顶点之间的最短路径长度,分量A[i][j]表示当前顶点i到顶点j到最短路径长度。Floyd算法到基本思想是递推产生一个矩阵序列A0,A1,……,Ak,……,An,其中Ak[i][j]表示从顶点i到顶点j的路径上经过的顶点编号不大于k的最短路径长度。

算法描述

Floyd算法的思想可用如下的表达式来描述:
A − 1 [ i ] [ j ] = c o s t [ i ] [ j ] A_{-1}[i][j]=cost[i][j] A1[i][j]=cost[i][j]

A k + 1 [ i ] [ j ] = m i n ( A k [ i ] [ j ] , A k [ i ] [ k + 1 ] + A k [ k + 1 ] [ j ] ) ( − 1 ≤ k ≤ n − 2 ) A_{k+1}[i][j]=min(A_{k}[i][j],A_{k}[i][k+1]+A_{k}[k+1][j])(-1\leq k\leq n-2) Ak+1[i][j]=min(Ak[i][j],Ak[i][k+1]+Ak[k+1][j])(1kn2)

图0

图2

以上图为例,初始时构建最短路径长度邻接矩阵如下:

ABCDEFG
A012***1614
B12010**7*
C*100356*
D**304**
E**54028
F1676*209
G14***890

第一步,以顶点A为中介点更新矩阵。

图3

第二步,以顶点B为中介点更新矩阵。

图4

第三步,以顶点C为中介点更新矩阵。

图5

第四步,以顶点D为中介点更新矩阵。

图6

第五步,以顶点E为中介点更新矩阵。

图7

第六步,以顶点F为中介点更新矩阵。

图8

第七步,以顶点G为中介点更新矩阵。

图9

public class Floyd {
    public static int max = 99999;

    public static void main(String[] args) {
        int[][] graph = {{0, 12, max, max, max, 16, 14},
                {12, 0, 10, max, max, 7, max},
                {max, 10, 0, 3, 5, 6, max},
                {max, max, 3, 0, 4, max, max},
                {max, max, 5, 4, 0, 2, 8},
                {16, 7, 6, max, 2, 0, 9},
                {14, max, max, max, 8, 9, 0}};
        floyd(graph);
        for (int i = 0; i < graph.length; i++) {
            for (int j = 0; j < graph.length - 1; j++) {
                System.out.print(graph[i][j] + ",");
            }
            System.out.println(graph[i][graph.length - 1]);
        }
    }

    public static void floyd(int[][] graph) {
        for (int i = 0; i < graph.length; i++) {//  floyd。
            for (int j = 0; j < graph.length; j++) {
                for (int k = 0; k < graph.length; k++) {
                    if (graph[j][i] + graph[i][k] < graph[j][k]) {
                        graph[j][k] = graph[j][i] + graph[i][k];
                    }
                }
            }
        }
    }
}

弗洛伊德算法的时间复杂的O(n^3),n为顶点数。

例子

POJ 1062.
POJ 1125.

  • 91
    点赞
  • 373
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值