数据结构——图最短路径问题

3 篇文章 0 订阅
2 篇文章 0 订阅

最短路径

我们把边带有权值的图称为带权图,边的权值可以表示
两点之间的距离。
一张图中任意两点间会有不同的路径相连。
最短路径就是指连接两点的这些路径中最短的一条。
在这里插入图片描述
有一点需要特别注意:边的权值可以为负。当出现负边权时,有些算法不适用。
dis[u][v]表示从u到v最短路径长度,w[u][v]表示连接u,v的边的长度。

Floyd-Warshall算法

简称Floyd(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。
Floyed的时间复杂度是O (n^3),适用于出现负边权的情况。
算法思想:
考虑任意两点(u和v)间的所有可能路径情况:
u直接到达v {u,v}。
u经过其他点到达v{u, vi, ……, v}。

算法策略:
如何穷尽Vi 到 Vj 的所有可能存在的路径?
可能的路径情况:
如果Vi 和Vj 邻接,{Vi , Vj }
通过一个其他顶点中转,表示为{Vi , Vk , Vj } ,其中k≠i、j。
通过两个其他顶点中转,可表示为
{Vi , Vk , Vl, Vj },其中k、l 与i、j均不等。
依次类推, Vi 到Vj 的路径最多可能通过n-2 个顶点中转可达,即除了路径的起点、终点,其他顶点都可能出现在该路径中。
在这里插入图片描述
Floyd算法设计:
1.利用矩阵完成路径检测过程

矩阵D(i) 包含中转顶点不超过 i(即中转顶点从0到 i) 的所有最短路径长度。

矩阵D(0) D(0)[i][j]:从V[i]到V[j],中间顶点可以是V[0]的最短路径长度。V[0]因为不确定是从0开始的还是从1开始的第一个点。所以用V[0]代替。

矩阵D(k) D(k)[i][j]:从V[i]到V[j],中间顶点序号不大于k的(即从V[0]到k)最短路径长度。

不断更新矩阵D。
……
直到最后D(n-1) D (n-1)[i][j]: 从V[I]到V[j]的最短路径长度。
2.路径长度矩阵序列求解过程
首先,设置初始状态:D(-1)(邻接矩阵)
然后,通过递推,求解每一个D(i)
矩阵D(k-1) --------> 矩阵D(k)过程
若不存在以k为中转点的vi到vj的最短路径:
D(k)[i][j] = D(k-1)[i][j];
若存在以k的vi到vj的最短路径:
D(k)[i][j] = D(k-1)[i][k] + D(k-1)[k][j];
3.如何记录最短路径的顶点序列?
用矩阵P(二维数组)来存储路径
P[i][j]存储当前最短路径更新时的中转点
最后以递归的方法来输出整个路径

大致代码

void Floyd( )
 {
  for( i=1; i<=n; ++i)
    for(j=1; j<=n; ++j) 
    {
       D[i][j] = G[i][j];    //第一步,初始化:用图G 的邻接矩阵初始化二维数组D。二维数组P 的元素初始化。    
       P[i][j] = -1;
    }
  for(k = 1; k<=n; ++k)
    for(i=1; i<=n; ++i)
      for(j=1; j<=n; ++j)

           if(D[i][j] >D[i][k] + D[k][j] )//第二步,对每个顶点Vk,若D[i][k]+D[k][j]< D[i][j],则用D[i][k]+D[k][j]更新D[i][j],并且修改P[i][j]为k。
          {
              D[i][j] = D[i][k] + D[k][j] ;          P[i][j] = k;
           }
}

利用数组P进行递归输出各条最短路径:
比如,输出B到A的路径:
因为P[2][1]为4,说明这条路径经过了4号顶点,即:顶点D。因此它是由B到D的最短路和D到A的最短路构成;
同样根据P[2][4]是3,可知它由B到C的最短路和C到D最短路构成,以此类推,因此通过递归就可以输出该条路径。
在这里插入图片描述

void print(int i,  int  j)
{
  if (P[i][j] == -1) return; 
  print( i,  P[i][j]); //前半段路
    cout << P[i][j]<<"->" ;
     print( P[i][j] , j); //后半段路

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Dijkstra算法可以用来求带权有向上的最短路径,下面是Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; // 定义无穷大 public static void dijkstra(int[][] graph, int start) { int n = graph.length; // 的大小 int[] dist = new int[n]; // 存储起点到各个点的最短距离 boolean[] visited = new boolean[n]; // 标记各个结点是否已经访问过 int[] prev = new int[n]; // 存储到达各个结点的前驱结点 // 初始化 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; prev[i] = -1; } dist[start] = 0; // 循环n-1次,每次确定一个顶点的最短路径 for (int i = 0; i < n - 1; i++) { int minDist = MAX; int u = -1; // 找到当前未访问的结点中距离起点最近的结点 for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = true; // 更新与u相邻的结点的最短距离 for (int j = 0; j < n; j++) { if (!visited[j] && graph[u][j] != MAX) { int newDist = dist[u] + graph[u][j]; if (newDist < dist[j]) { dist[j] = newDist; prev[j] = u; } } } } // 输出结果 System.out.println("起点为" + start + "的最短路径如下:"); for (int i = 0; i < n; i++) { if (i != start && dist[i] != MAX) { System.out.print("从" + start + "到" + i + "的最短路径为:" + start); int j = i; while (j != start) { System.out.print(" -> " + j); j = prev[j]; } System.out.println(",路径长度为:" + dist[i]); } } } public static void main(String[] args) { int[][] graph = { {0, 1, 12, MAX, MAX}, {MAX, 0, 9, 3, MAX}, {MAX, MAX, 0, MAX, 5}, {MAX, MAX, 4, 0, 13}, {MAX, MAX, MAX, MAX, 0} }; dijkstra(graph, 0); } } ``` 运行结果: ``` 起点为0的最短路径如下: 从0到1的最短路径为:0 -> 1,路径长度为:1 从0到2的最短路径为:0 -> 1 -> 3 -> 2,路径长度为:12 从0到3的最短路径为:0 -> 1 -> 3,路径长度为:4 从0到4的最短路径为:0 -> 1 -> 3 -> 2 -> 4,路径长度为:17 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杜康o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值