3112.力扣每日一题7/18 Java 迪杰斯特拉(Dijkstra)算法

  • 博客主页:音符犹如代码
  • 系列专栏:算法练习
  • 关注博主,后期持续更新系列文章
  • 如果有错误感谢请大家批评指出,及时修改
  • 感谢大家点赞👍收藏⭐评论✍

目录

迪杰斯特拉(Dijkstra)算法

解题思路

解题过程

时间复杂度

空间复杂度

Code


迪杰斯特拉(Dijkstra)算法

迪杰斯特拉(Dijkstra)算法是一种用于求解单源最短路径问题的算法。

它的基本思想是:从一个起始顶点出发,逐步向外扩展,每次选择距离起始顶点最近且未被处理过的顶点,然后更新该顶点相邻顶点的距离。

以下是迪杰斯特拉算法的具体步骤:

  1. 初始化:

    • 为每个顶点设置一个初始距离值。起始顶点的距离设为 0,其他顶点的距离设为无穷大。
    • 创建一个标记数组,用于标记顶点是否已处理。
  2. 选择当前未处理且距离最小的顶点:

    • 从所有未处理的顶点中,选择距离起始顶点最近的顶点。
  3. 更新相邻顶点的距离:

    • 对于所选顶点的相邻顶点,计算通过当前顶点到达相邻顶点的新距离。如果新距离小于原来的距离,则更新相邻顶点的距离。
  4. 标记所选顶点已处理:

    • 将所选顶点标记为已处理。
  5. 重复步骤 2 - 4,直到所有顶点都被处理。

最终,每个顶点的距离就是从起始顶点到该顶点的最短路径长度。

迪杰斯特拉算法的时间复杂度通常为 O(N²),其中N是顶点的数量。如果使用二叉堆如果使用二堆(或斐波那契堆)来优化选择最小距离顶点的操作,时间复杂度可以优化到O((N+M)logN),其中M是边的数量

解题思路

使用迪杰斯特拉(Dijkstra)算法的思想,通过优先级队列来找到从节点 0 到其他节点的最短时间。同时考虑每个节点的消失时间,如果在节点消失之前无法到达,则标记为不可达(-1)。

解题过程

  1. 首先构建图的邻接表表示。
  2. 初始化每个节点的最少到达时间为 -1,节点 0 的到达时间为 0 ,并创建优先级队列。
  3. 当优先级队列不为空时,取出队首元素。
  4. 对于取出节点的每个邻居节点,计算新的到达时间。如果新的到达时间小于邻居节点的消失时间,并且比之前记录的到达时间更短,就更新邻居节点的到达时间,并将其加入优先级队列。

时间复杂度

主要的时间消耗在于优先级队列的操作和对图的遍历。在最坏情况下,对于一个包含 n 个节点和 m 条边的图,时间复杂度为 O((n + m) log n) 。但在这个问题中,由于每个节点最多被处理一次,并且边也只会被访问有限次,所以更接近 O(n log n)

空间复杂度

使用了邻接表来存储图,空间复杂度为 O(n + m) ,还使用了一个长度为 n 的数组来存储每个节点的最少到达时间和一个优先级队列,空间复杂度主要取决于节点数量 n 和边数量 m ,大致为 O(n + m) 。

Code

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
    public int[] minimumTime(int numNodes, int[][] edgeInfos, int[] vanishTimings) {
        // 使用邻接表表示图
        ArrayList<int[]>[] graph = new ArrayList[numNodes]; 
        Arrays.setAll(graph, i -> new ArrayList<>());

        // 构建图
        for (int[] edge : edgeInfos) {
            int nodeA = edge[0];
            int nodeB = edge[1];
            int weight = edge[2];
            graph[nodeA].add(new int[]{nodeB, weight});
            graph[nodeB].add(new int[]{nodeA, weight});
        }

        // 存储每个节点的最少到达时间
        int[] minReachTimes = new int[numNodes]; 
        Arrays.fill(minReachTimes, -1);
        minReachTimes[0] = 0;

        // 优先级队列,按到达时间排序
        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); 
        priorityQueue.offer(new int[]{0, 0});

        while (!priorityQueue.isEmpty()) {
            int[] current = priorityQueue.poll();
            int currentReachTime = current[0];
            int currentNode = current[1];

            // 如果当前节点之前已经有更短的到达时间,跳过
            if (currentReachTime > minReachTimes[currentNode]) { 
                continue;
            }

            for (int[] neighbor : graph[currentNode]) {
                int neighborNode = neighbor[0];
                int newReachTime = currentReachTime + neighbor[1];

                // 如果新的到达时间小于邻居节点消失时间,并且比之前记录的到达时间更短
                if (newReachTime < vanishTimings[neighborNode] && (minReachTimes[neighborNode] < 0 || newReachTime < minReachTimes[neighborNode])) { 
                    minReachTimes[neighborNode] = newReachTime; 
                    priorityQueue.offer(new int[]{newReachTime, neighborNode});
                }
            }
        }

        return minReachTimes;
    }
}

人生实如钟摆,在痛苦与倦怠之间摆动——叔本华(Arthur Schopenhauer)

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值