- 博客主页:音符犹如代码
- 系列专栏:算法练习
- 关注博主,后期持续更新系列文章
- 如果有错误感谢请大家批评指出,及时修改
- 感谢大家点赞👍收藏⭐评论✍
目录
迪杰斯特拉(Dijkstra)算法
解题思路
解题过程
时间复杂度
空间复杂度
Code
迪杰斯特拉(Dijkstra)算法
迪杰斯特拉(Dijkstra)算法是一种用于求解单源最短路径问题的算法。
它的基本思想是:从一个起始顶点出发,逐步向外扩展,每次选择距离起始顶点最近且未被处理过的顶点,然后更新该顶点相邻顶点的距离。
以下是迪杰斯特拉算法的具体步骤:
-
初始化:
- 为每个顶点设置一个初始距离值。起始顶点的距离设为 0,其他顶点的距离设为无穷大。
- 创建一个标记数组,用于标记顶点是否已处理。
-
选择当前未处理且距离最小的顶点:
- 从所有未处理的顶点中,选择距离起始顶点最近的顶点。
-
更新相邻顶点的距离:
- 对于所选顶点的相邻顶点,计算通过当前顶点到达相邻顶点的新距离。如果新距离小于原来的距离,则更新相邻顶点的距离。
-
标记所选顶点已处理:
- 将所选顶点标记为已处理。
-
重复步骤 2 - 4,直到所有顶点都被处理。
最终,每个顶点的距离就是从起始顶点到该顶点的最短路径长度。
迪杰斯特拉算法的时间复杂度通常为 O(N²),其中N是顶点的数量。如果使用二叉堆如果使用二堆(或斐波那契堆)来优化选择最小距离顶点的操作,时间复杂度可以优化到O((N+M)logN),其中M是边的数量
解题思路
使用迪杰斯特拉(Dijkstra)算法的思想,通过优先级队列来找到从节点 0 到其他节点的最短时间。同时考虑每个节点的消失时间,如果在节点消失之前无法到达,则标记为不可达(-1)。
解题过程
- 首先构建图的邻接表表示。
- 初始化每个节点的最少到达时间为 -1,节点 0 的到达时间为 0 ,并创建优先级队列。
- 当优先级队列不为空时,取出队首元素。
- 对于取出节点的每个邻居节点,计算新的到达时间。如果新的到达时间小于邻居节点的消失时间,并且比之前记录的到达时间更短,就更新邻居节点的到达时间,并将其加入优先级队列。
时间复杂度
主要的时间消耗在于优先级队列的操作和对图的遍历。在最坏情况下,对于一个包含 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;
}
}