【数据结构】什么是图的最短路径?实现最短路径的2种算法?

简介: 【数据结构】什么是图的最短路径?实现最短路径的2种算法?

一、什么是最短路径?


最短路径问题是指在一个赋权图的两个节点之间找出一个具有最小权的路径。旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。


现实生活中我们可用看到许多最短路径问题的例子:


如公交车辆的最优行驶路线和旅游线路的选择。

军事领域中,作战部队的行军陆路线。

救护车、消防车等救援车辆采取最短行驶路线火速赶往现场。

以上等问题,都是在寻找一个的最短路径作为最优选择;而这就与寻找一个图的最短路径密切相关。


二、实现最短路径的2种算法?


用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”。


最常用的路径算法有:


1、迪 杰斯特拉(Dijkstra)算法:


求单源最短路径的。


 从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。


2、佛洛依德(Floyed) 算法:


是解决任意两点间的最短路径的一种算法,用来求图中所有点对之间的最短路径。


三、最短路径


1、某个顶点到其余各顶点的最短路径:迪 杰斯特拉(Dijkstra)算法


1)概述


网经常用于描述一个城市或城市间的交通运输网络。


以顶点表示一个城市或某个交通枢纽


以边表示两地之间的交通状况


边上的权值表示各种相关信息


当两个顶点之间存在多条路径时,其中必然存在一条“最短路径”


名词:


源点(Source):路径中的第一个顶点


终点(Destination):最后一个顶点


2)实例


852cd0b35fed45378b0febf4682418fb.png

从源点v0到终点v5存在多条路径:


v0, v5) 的长度为 100


(v0, v4, v5) 的长度为 90


(v0,v4,v3 v5) 的长度为 60   //最短路径


(v0, v2,v3, v5) 的长度为 7


从源点v0到各终点的最短路径:


v0到终点v1不存在路径


(v0,v2) 的最短路径长度为10


(v0,v4,v3) 的最短路径长度为50


(v0,v4) 的最短路径长度为30


(v0,v4,v3,v5) 的最短路径长度为60


3)迪杰斯特拉(Dijkstra)算法:概述


算法描述:


1.用于求解某个源点到其余各顶点的最短路径。


2.按最短路径长度递增的次序求解


3.类似于普利姆算法


每一条最短路径必定只有两种情况:


1.由源点直接到达终点


2.只经过已经求得最短路径的顶点到达终点


迪杰斯特拉算法解决的问题:如何实现“按最短路径长度递增的次序”求解


解决方法:


1.每次选出当前的一条最短路径


2.算法中需要引入一个辅助向量D,它的每个分类D[i]存放当前所找到的从源点到各个终点vi的最短路径长度。


4)迪杰斯特拉(Dijkstra)算法:算法分析


初始若从源点v到各个终点有弧,则存在一条路径,路径长度即为该弧上的权值,保存于D中


求得一条到达某个终点的最短路径,即当前路径中的最小值,该顶点即为w


检查是否存在经过顶点w的其他路径,若存在,判断其长度是否比当前求得的路径长度短,若是,则修改D中当前最短路径。


重复上面的2和3


c9294c5f711347e29f30559d53510f45.png


5)迪杰斯特拉(Dijkstra)算法:代码实现


测试数据:


1c101b168b344626bdbbaec2c95f4daa.png


处理后的数组P的内容:


274003da2225468d8271c9f05b6b5214.png


处理后数组D的内容:


81fe24df81404ecd9b410cf49bb8f41a.png



算法代码实现:

public class ShortestPath_DIJ {
   private boolean[][] P;// v0到其余顶点的最短路径, 若P[v][w]为true,则w是从v0到v当前求得最短路径上的顶点
   private int[] D;// v0到其余顶点的带权长度
   public final static int INFINITY = Integer.MAX_VALUE;
   // 用Dijkstra算法求有向网G的v0顶点到其余顶点v的最短路径P[v]及其权值D[v]
   public void DIJ(MGraph G, int v0) {
      int vexNum = G.getVexNum();
      P = new boolean[vexNum][vexNum];  // 用于记录经过的顶点
      D = new int[vexNum];        // 用于记录到达某顶点需要的最短路径和
      boolean[] finish = new boolean[vexNum];// finish[v]为true当且仅当v属于S,即已经求得从v0到v的最短路径
      // 数据初始化
      for (int v = 0; v < vexNum; v++) {
         finish[v] = false;
         D[v] = G.getArcs()[v0][v];   // D 记录v0到所有各个边的权值
         for (int w = 0; w < vexNum; w++)
            P[v][w] = false;      // 设空路径,及设置默认值
         if (D[v] < INFINITY) {
            P[v][v0] = true;      // 在P数组的第v行,标记v0被访问
            P[v][v] = true;       // 在P数组的第v行,标记邻接点被访问
         }
      }
      D[v0] = 0;// 初始化,v0顶点属于S集
      finish[v0] = true;
      int v = -1;
      // 开始主循环,每次求得v0到某个v顶点的最短路径,并加v到S集
      for (int i = 1; i < vexNum; i++) {// 其余G.getVexNum-1个顶点
         int min = INFINITY;// 当前所知离v0顶点的最近距离
         for (int w = 0; w < vexNum; w++)
            if (!finish[w])
               if (D[w] < min) {
                  v = w;
                  min = D[w];
               }
         finish[v] = true;// 离v0顶点最近的v加入S集
         for (int w = 0; w < vexNum; w++)
            // 更新当前最短路径及距离
            if (!finish[w] && G.getArcs()[v][w] < INFINITY
                  && (min + G.getArcs()[v][w] < D[w])) { // 修改D[w]和P[w],w属于V-S
               D[w] = min + G.getArcs()[v][w];
               // w在v的基础上继续前行,所以将v经过的内容拷贝到w上
               System.arraycopy(P[v], 0, P[w], 0, P[v].length);
               P[w][w] = true;
            }
      }
   }
   public int[] getD() {
      return D;
   }
   public boolean[][] getP() {
      return P;
   }
   public static void main(String[] args) throws Exception {
      MGraph G = GenerateGraph.generateMGraph();
      ShortestPath_DIJ dij = new ShortestPath_DIJ();
      dij.DIJ(G, 0);
      dij.display(G);
   }
   // 用于输出最短路径上的各个顶点及权值
   public void display(MGraph G) throws Exception {
      if (D != null) {
         System.out.println("各个顶点到v0的最短路径上的点及最短距离分别是:");
         for (int i = 0; i < P.length; i++) {
            System.out.print("v0 - " + G.getVex(i) + ": ");
            for (int j = 0; j < P[i].length; j++) {
               if (P[i][j])
                  System.out.print(G.getVex(j) + "\t");
            }
            System.out.println("最短距离为: " + D[i]);
         }
      }
   }
}
// 调试结果:
// 各个顶点到v0的最短路径上的点及最短距离分别是:
// v0 - v0: v0 最短距离为: 0
// v0 - v1: v0 v1 最短距离为: 7
// v0 - v2: v0 v2 最短距离为: 1
// v0 - v3: v0 v3 最短距离为: 5
// v0 - v4: v0 v2 v4 最短距离为: 7
// v0 - v5: v0 v2 v5 最短距离为: 5

6)迪杰斯特拉(Dijkstra)算法:性能分析


算法的时间复杂度:O(n2)


找一条从源点到某一特定终点之间的最短路径问题和求源点到各个终点的最短路径一样复杂,其时间复杂度仍为O(n2)


若希望得到图中任意两个顶点之间的最短路径,只要依次将每一个顶点设为源点,并调用迪杰斯特拉算法,其时间复杂度为 O(n3)


2、每一对顶点之间的最短路径: 佛洛依德(Floyed) 算法


1)概述


若希望得到图中任意两个定点之间的最短路径,只要依次将每一个定点设为源点,调用迪杰斯克拉算法n次便可求得,其时间复杂度为O(n3) 。


佛洛依德(Floyed)提出了另外一个算法,虽然时间复杂度也是O(n3),但是算法的形式更为简单。


2)佛洛依德(Floyed) 算法:概述


D是路径长度序列


P是路径序列


1. 求的一个n阶方阵序列:D(-1),D(0),D(1),D(2),... ,D(k),...,D(n-1)


2. D(-1)[i][j]表示从顶点vi出发,不经过其他顶点【直接】到达顶点vj的路径长度。


3. D(k)[i][j]表示从顶点vi到vj的中间只可能经过v0,v1,....,vk , 而不可能经过 vk+1,vk+2, ... ,vn-1等顶点的最短路径长度。


4. D(n-1)[i][j]表示从顶点vi到顶点vj的最短路径的长度。


3)佛洛依德(Floyed) 算法:算法分析


算法关键操作

// k 表示在路径中新增的顶点号
// i 表示路径的源点
// j 表示路径的终点
if(D[i][k] + D[k][j] < D[i][j]) {
    D[i][j] = D[i][k] + D[k][j];    //更新最短路径长度
    P[i][j] = P[i][k] + P[k][j];    //更新最短路径
}
  • 有向图

a092d64323e74fce9a2cf5226ec1e583.png

4)佛洛依德(Floyed) 算法:代码实现

  • 类似于对每一个顶点进行迪杰斯特拉算法。

fbca0edaa1b444a48520a581c10fae2b.png

  • 算法代码实现:
public class ShortestPath_FLOYD {
   private boolean[][][] P;// 顶点v和w之间的最短路径P[v][w],若P[v][w][u]为true,则u是从v到w当前求得最短路径上的顶点
   private int[][] D;// 顶点v和w之间最短路径的带权长度D[v][w]
   public final static int INFINITY = Integer.MAX_VALUE;
   // 用Dijkstra算法求有向网G的v0顶点到其余顶点v的最短路径P[v]及其权值D[v]
   public void FLOYD(MGraph G) {
      int vexNum = G.getVexNum();
      P = new boolean[vexNum][vexNum][vexNum];
      D = new int[vexNum][vexNum];
      for (int v = 0; v < vexNum; v++) // 各对结点之间初始化已知路径及距离
         for (int w = 0; w < vexNum; w++) {
            D[v][w] = G.getArcs()[v][w];
            for (int u = 0; u < vexNum; u++)
               P[v][w][u] = false;
            if (D[v][w] < INFINITY) {// 从v到w有直接路径
               P[v][w][v] = true;
               P[v][w][w] = true;
            }
         }
      for (int u = 0; u < vexNum; u++)
         for (int v = 0; v < vexNum; v++)
            for (int w = 0; w < vexNum; w++)
               if (D[v][u] < INFINITY && D[u][w] < INFINITY
                     && D[v][u] + D[u][w] < D[v][w]) { // 从v经u到w的一条路径最短
                  D[v][w] = D[v][u] + D[u][w];
                  for (int i = 0; i < vexNum; i++)
                     P[v][w][i] = P[v][u][i] || P[u][w][i];
               }
   }
   public int[][] getD() {
      return D;
   }
   public boolean[][][] getP() {
      return P;
   }
}

测试程序:

public class Example6_5_G9 {
  public final static int INFINITY = Integer.MAX_VALUE;
  public static void main(String[] args) throws Exception {
    Object vexs[] = { "v0", "v1", "v2" };
    int[][] arcs = { { 0, 4, 11},
            { 6, 0, 2},
            { 3, INFINITY,0}};
    MGraph G = new MGraph(GraphKind.DN, vexs.length, 5, vexs, arcs);
    ShortestPath_FLOYD floyd = new ShortestPath_FLOYD();
    floyd.FLOYD(G);
    display(floyd.getD());
  }
  // 输出各村的最短路径长度
  public static void display(int[][] D) {
    System.out.println("各顶点之间的最短路径长度为:");
    for (int v = 0; v < D.length; v++) {
      for (int w = 0; w < D.length; w++)
        System.out.print(D[v][w] + "\t");
      System.out.println();
    }
  }
}
// 调试结果:
//各顶点之间的最短路径长度为:
//    0 4 6
//    5 0 2
//    3 7 0

5)佛洛依德(Floyed) 算法:性能分析


算法复杂度仍为O(n3)


是解决任意两点间的最短路径的一种算法


求单源点无负边最短路径用Dijkstra,而求所有点最短路径用Floyd且Floyd可以处理带负边的图。


对于稀疏的图,采用n次Dijkstra比较出色,对于茂密的图,可以使用Floyd算法。


从它的三层循环可以看出,它的复杂度是n3,除了在第二层for中加点判断可以略微提高效率,几乎没有其他办法再减少它的复杂度。


四、练习


1、迪杰斯特拉(Dijkstra)算法练习:

90eca4e584a74b3cae31f32b8ead4e48.png


90eca4e584a74b3cae31f32b8ead4e48.png

①②③④⑤⑥⑦⑧


(①,②) 最短路径:4


(①,④) 最短路径:2


(①,④,③) 最短路径:3


(①,④,③,⑤) 最短路径:6


(①,④,③,⑦) 最短路径:6


(①,④,③,⑤,⑥) 最短路径:8


(①,④,③,⑤,⑧) 最短路径:18


2、佛洛依德(Floyed) 算法练习:


4f16933f07fe44b284ebd9918ff03191.png


推导过程:

992e0c4cef7f4a529df6702f5410a5de.png

相关文章
|
26天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
38 1
|
29天前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
97 4
|
4天前
|
存储 运维 监控
探索局域网电脑监控软件:Python算法与数据结构的巧妙结合
在数字化时代,局域网电脑监控软件成为企业管理和IT运维的重要工具,确保数据安全和网络稳定。本文探讨其背后的关键技术——Python中的算法与数据结构,如字典用于高效存储设备信息,以及数据收集、异常检测和聚合算法提升监控效率。通过Python代码示例,展示了如何实现基本监控功能,帮助读者理解其工作原理并激发技术兴趣。
42 20
|
27天前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
|
27天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
1月前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
102 23
|
1月前
|
算法
数据结构之蜜蜂算法
蜜蜂算法是一种受蜜蜂觅食行为启发的优化算法,通过模拟蜜蜂的群体智能来解决优化问题。本文介绍了蜜蜂算法的基本原理、数据结构设计、核心代码实现及算法优缺点。算法通过迭代更新蜜蜂位置,逐步优化适应度,最终找到问题的最优解。代码实现了单链表结构,用于管理蜜蜂节点,并通过适应度计算、节点移动等操作实现算法的核心功能。蜜蜂算法具有全局寻优能力强、参数设置简单等优点,但也存在对初始化参数敏感、计算复杂度高等缺点。
60 20
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
58 1
|
1月前
|
机器学习/深度学习 算法 C++
数据结构之鲸鱼算法
鲸鱼算法(Whale Optimization Algorithm,WOA)是由伊朗研究员Seyedali Mirjalili于2016年提出的一种基于群体智能的全局优化算法,灵感源自鲸鱼捕食时的群体协作行为。该算法通过模拟鲸鱼的围捕猎物和喷出气泡网的行为,结合全局搜索和局部搜索策略,有效解决了复杂问题的优化需求。其应用广泛,涵盖函数优化、机器学习、图像处理等领域。鲸鱼算法以其简单直观的特点,成为初学者友好型的优化工具,但同时也存在参数敏感、可能陷入局部最优等问题。提供的C++代码示例展示了算法的基本实现和运行过程。
51 0
|
1月前
|
算法 vr&ar 计算机视觉
数据结构之洪水填充算法(DFS)
洪水填充算法是一种基于深度优先搜索(DFS)的图像处理技术,主要用于区域填充和图像分割。通过递归或栈的方式探索图像中的连通区域并进行颜色替换。本文介绍了算法的基本原理、数据结构设计(如链表和栈)、核心代码实现及应用实例,展示了算法在图像编辑等领域的高效性和灵活性。同时,文中也讨论了算法的优缺点,如实现简单但可能存在堆栈溢出的风险等。
43 0

热门文章

最新文章