首页 > 编程语言 >Dijkstra最短路径算法及其优化

Dijkstra最短路径算法及其优化

时间:2023-08-06 20:56:59浏览次数:44  
标签:weight int MAX 算法 最短 start Dijkstra res length

Dijkstra最短路径算法及其优化

图示过程可以参考图文详解 Dijkstra 最短路径算法 (freecodecamp.org)

直接给出Java版本的普通实现方式:

/**
 * 最普通的实现形式,时间复杂度是O(n2),空间复杂度是O(n)
 * @param weight
 * @param start
 * @return
 */
public static int[] getDijkstraRes(int[][] weight,int start){
    boolean[] visited = new boolean[weight.length];
    int[] res = new int[weight.length];
    for (int i = 0;i < res.length;i++){
        res[i] = weight[start][i];
    }
    // 查找 n - 1 次(n 为节点个数),每次确定一个节点
    for (int i = 1;i < weight.length;i++){
        int min = Integer.MAX_VALUE;
        int closedNode = 0;
        // 找出一个未标记的离出发点最近的节点
        for (int j = 0;j < weight.length;j++){
            if (j != start && !visited[j] && res[j] < min){
                min = res[j];
                closedNode = j;
            }
        }
        // 标记该节点为已经访问过
        visited[closedNode] = true;
        //根据加入的节点更新res数组
        for(int j = 0;j < weight.length;j++){
            if (j == closedNode || weight[closedNode][j] == Integer.MAX_VALUE){
                //与新添加进来的节点也是不可达的,就跳过
                continue;
            }
            if (res[closedNode] + weight[closedNode][j] < res[j]){
                //更新操作
                res[j] = res[closedNode] + weight[closedNode][j];
            }
        }
    }
    return res;
}

​ 该方法是最普通的实现方式,时间复杂度在O(n^2),空间复杂度为O(n),那么这个算法是否有可疑优化的地方呢?

答案是有的,主要可以优化的点在于:

  // 找出一个未标记的离出发点最近的节点
  for (int j = 0;j < weight.length;j++){
      if (j != start && !visited[j] && res[j] < min){
          min = res[j];
          closedNode = j;
      }
  }

​ 在这个循环中,需要找到未访问过的且与已被访问的节点相连的最短的点,需要遍历所有的边,即O(n),这里我们使用小根堆,就可以实现插入O(logn),读取O(1)的时间复杂度,可以使得时间复杂度降低。

/**
 * 作为小根堆里面元素的载体
 */
static class Edge implements Comparable<Edge>{
    int val;
    int to;
    @Override
    public int compareTo(Edge o) {
        //当本val大于传入的o的val时,返回正值,即本Edge应该往后面排
        return this.val - o .val;
    }
    Edge(int _val,int _to){
        val = _val;
        to = _to;
    }
}

/**
 * 使用了优先队列来选出与已访问集合相连的最短的节点,
 * 这样方式的时间复杂度是O((n+m)logn),会比遍历求最短节点的方法时间复杂度低
 * @param weight
 * @param start
 * @return
 */
public static int[] getDijkstraRes2(int[][] weight,int start){
    int[] res = new int[weight.length];
    boolean[] visited = new boolean[weight.length];
    for (int i = 0;i < weight.length;i++){
        if(i != start){
            res[i] = Integer.MAX_VALUE;
        }
    }
    Queue<Edge> queue = new PriorityQueue<>();
    Edge firstEdge = new Edge(0,start);
    queue.add(firstEdge);
    while (!queue.isEmpty()){
        //把最小的那个拿出来,此时我就不用遍历
        Edge pollEdge = queue.poll();
        int node = pollEdge.to;
        int val = pollEdge.val;
        if(visited[node]){
            //已经访问过了,就不要再访问了
            continue;
        }
        res[node] = val;
        visited[node] = true;

        for (int j = 0;j < weight.length;j++){
            if (!visited[j]
                    && weight[node][j] != Integer.MAX_VALUE //必须加上这个,否则下面相加可能会溢出导致判断错误
                    && res[j] > res[node] + weight[node][j]){
                res[j] = res[node] + weight[node][j];
                queue.add(new Edge(res[j],j));
            }
        }
    }
    return res;
}

​ 在上面的这个版本中,时间复杂度为O((n+m)logn),其中m是边的条数,n是点的个数。

用法举例:

public static void main(String[] args) {
    int MAX = Integer.MAX_VALUE;    // 无法到达时距离设为 Integer.MAX_VALUE
    int[][] weight={
            {0,1,12,MAX,MAX,MAX},
            {MAX,0,9,3,MAX,MAX},
            {MAX,MAX,0,MAX,5,MAX},
            {MAX,MAX,4,0,13,15},
            {MAX,MAX,MAX,MAX,0,4},
            {MAX,MAX,MAX,MAX,MAX,0}
    };
    int start = 0;  // 选择出发点
    System.out.println(Arrays.toString(getDijkstraRes(weight, start)));
    System.out.println(Arrays.toString(getDijkstraRes2(weight, start)));
}

标签:weight,int,MAX,算法,最短,start,Dijkstra,res,length
From: https://www.cnblogs.com/csq-66/p/17610001.html

相关文章

  • 【学习笔记】类欧几里得算法
    概述主要是求以下三个式子:\[f(a,b,c,n)=\sum_{i=0}^n\left\lfloor\dfrac{ai+b}{c}\right\rfloor\]\[g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor\dfrac{ai+b}{c}\right\rfloor\]\[h(a,b,c,n)=\sum_{i=0}^n\left\lfloor\dfrac{ai+b}{c}\right\rfloor^2\]求\(f......
  • 算法 华为
     1、链表,两两交换位置,不允许修改值,只能改节点例如1234,=>21432、拔河比赛选拔队员,输入身高,体重。按这两个优先级排序例如输入1827019060输出1906019060 3、最小花费问题(这个分值200,比前面的难)输入产品数量n,需要输出k种方案n个产品对应的价格数组输出:前k小......
  • 数据结构与算法(四):双向链表
    基本概念双向链表概念和单向链表是一致的,区别在于双向链表在单向链表的基础上,指针区域多了一个指向上一个节点的指针。单向链表内容可以参考我的上一篇文章:http://t.csdn.cn/Iu56H。基本的数据结构如图所示:链表结构双向链表结构包含了节点的数据内容和两个指针:指向前一个节点......
  • m基于FFT傅里叶变换的QPSK基带信号频偏估计和补偿算法FPGA实现,包含testbench和matlab
    1.算法仿真效果本系统进行了Vivado2019.2平台的开发,并使用matlab2022a对结果进行星座图的显示:将FPGA的频偏基带QPSK信号和频偏补偿后的QPSK基带信号使用matlab显示星座图,结果如下:2.算法涉及理论知识概要QPSK(QuadraturePhaseShiftKeying)是一种常用的调制方式,它可以在相位和......
  • m基于FFT傅里叶变换的QPSK基带信号频偏估计和补偿算法FPGA实现,包含testbench和matlab
    1.算法仿真效果本系统进行了Vivado2019.2平台的开发,并使用matlab2022a对结果进行星座图的显示:   将FPGA的频偏基带QPSK信号和频偏补偿后的QPSK基带信号使用matlab显示星座图,结果如下:   2.算法涉及理论知识概要       QPSK(QuadraturePhaseShiftKeying)......
  • 基于位相光栅的四波横向剪切干涉法波前检测算法的matlab仿真
    1.算法理论概述      波前检测技术是现代光学中的重要技术之一,可以用于衡量光学系统的成像质量和研究光学系统的异常现象。随着现代光学技术的不断发展,波前检测技术也在不断地发展和完善。其中,基于位相光栅的四波横向剪切干涉法波前检测算法是一种常用的波前检测算法,本文......
  • 最短路径Dijkstra算法--求距离和路径
    1、题目描述求单条路线2、AC代码#include<iostream>#include<cstring>#include<bits/stdc++.h>#defineinf1000000000usingnamespacestd;typedeflonglongll;constintN=105;intn,m,s,t;structnode{ intv;//边终点 intw;//边权 node(intx,inty)......
  • dijkstra + 单调栈优化
    打Div.3发现两个最短路板子题一个用的SPFA一个用的邻接矩阵,赶紧补个。#include<iostream>#include<queue>#defineMAXN20010usingnamespacestd;constintinf=2147483647;intn,m,s,t,cnt;intdis[MAXN],h[MAXN],to[MAXN],val[MAXN],nxt[MAXN];boolvis[MAXN]......
  • 代码随想录算法训练营第七天|力扣334.反转字符串、力扣541.反转字符串II、剑指offer05
    字符串反转字符串(力扣344.)如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。毕竟面试官一定不是考察你对库函数的熟悉程度,如果使用python和java的同学更需要注意这一点,因为python、java提供的库函数十分丰富。如果库函数仅仅是解题过程中的一小部分,并且......
  • 通往奥格瑞玛的道路(单源最短路+二分)
    //通往奥格瑞玛的道路//二分最大的答案,然后有单点超过这个值就直接返回,继续二分//每循环一次都要跑一遍最短路,这里选择时间复杂度更优的堆优化dijkstra//坑点的较多,还请注意#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e6+10,MAX=......