leetcode
leetcode 651 ~ 700
网络延迟时间

网络延迟时间

难度:

标签:

题目描述

n 个网络节点,标记为 1 到 n

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1

 

示例 1:

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
输出:2

示例 2:

输入:times = [[1,2,1]], n = 2, k = 1
输出:1

示例 3:

输入:times = [[1,2,1]], n = 2, k = 2
输出:-1

 

提示:

  • 1 <= k <= n <= 100
  • 1 <= times.length <= 6000
  • times[i].length == 3
  • 1 <= ui, vi <= n
  • ui != vi
  • 0 <= wi <= 100
  • 所有 (ui, vi) 对都 互不相同(即,不含重复边)

代码结果

运行时间: 45 ms, 内存: 17.7 MB


/*
 * Problem: Given a list of travel times between directed edges in a network, determine how long it will take for all nodes to receive a signal sent from a starting node K. If not all nodes can receive the signal, return -1.
 * Approach: Use Java Streams to process the input and apply Dijkstra's algorithm. The result will be the maximum time required for the signal to reach all nodes or -1 if not all nodes can be reached.
 */
import java.util.*;
import java.util.stream.Collectors;

public class NetworkDelayTimeStream {
    public int networkDelayTime(int[][] times, int n, int k) {
        // Create an adjacency list
        Map<Integer, List<int[]>> graph = Arrays.stream(times)
            .collect(Collectors.groupingBy(
                time -> time[0],
                Collectors.mapping(time -> new int[]{time[1], time[2]}, Collectors.toList())
            ));
        // PriorityQueue to get the minimum distance node
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
        pq.offer(new int[]{k, 0});
        Map<Integer, Integer> dist = new HashMap<>();
        dist.put(k, 0);
        // Dijkstra's algorithm
        while (!pq.isEmpty()) {
            int[] info = pq.poll();
            int node = info[0], time = info[1];
            if (time > dist.get(node)) continue;
            if (!graph.containsKey(node)) continue;
            for (int[] edge : graph.get(node)) {
                int neighbor = edge[0], d = time + edge[1];
                if (d < dist.getOrDefault(neighbor, Integer.MAX_VALUE)) {
                    dist.put(neighbor, d);
                    pq.offer(new int[]{neighbor, d});
                }
            }
        }
        // Find the maximum distance
        if (dist.size() != n) return -1;
        return dist.values().stream().max(Integer::compareTo).orElse(-1);
    }
}

解释

方法:

这个解决方案使用邻接矩阵来表示图,并通过一个修改版的迪杰斯特拉算法来找到从节点K到所有其他节点的最短路径。算法开始时,初始化一个距离数组,所有节点的距离设为无穷大,除了起点K,其距离设为0。然后使用一个布尔数组来跟踪每个节点的访问状态。对于每一次循环,选择一个未访问的且距离最小的节点作为当前节点,然后遍历其所有邻居,更新邻居的最短距离。这一过程重复n次,每次处理一个节点。最后,从距离数组中找出最大值,如果所有节点都已访问(即最大值不是无穷大),则返回此最大值作为结果,否则返回-1。

时间复杂度:

O(n^2)

空间复杂度:

O(n^2)

代码细节讲解

🦆
为什么在这个算法中选择使用邻接矩阵而不是邻接列表来表示图?
在这个算法中选择使用邻接矩阵而不是邻接列表的主要原因是因为邻接矩阵可以更方便地访问任意两个节点之间的直接连接信息,特别是在需要频繁查询特定节点对之间是否存在边以及边的权重时。邻接矩阵使得在迪杰斯特拉算法中更新节点距离和检查节点连接状态变得直观和快速。虽然邻接矩阵在稀疏图中可能会导致空间效率降低,但在本题中可能是为了简化实现或者因为节点数量不是特别大,所以空间复杂度可以接受。
🦆
在迪杰斯特拉算法中,如何确定当前节点的所有邻居节点,特别是在使用邻接矩阵表示图的情况下?
在使用邻接矩阵表示图时,确定当前节点的所有邻居节点非常直接。具体来说,可以通过遍历当前节点对应的行或列(取决于矩阵的行和列分别代表了什么)来检查每一个其他节点。如果当前节点为 i,那么我们可以遍历图矩阵 graph[i][j] 的所有元素,其中 j 是其他所有可能的节点索引。如果 graph[i][j] 不是无穷大,则节点 j 是节点 i 的邻居节点,并且存在一条从 i 到 j 的边,权重为 graph[i][j]。
🦆
算法中如何处理图中的环或者自环(即从一个节点指向自身的边)?
在迪杰斯特拉算法的实现中,图中的环或自环可以被自然地处理,特别是在邻接矩阵的表示下。对于自环的情况,即一个节点i到自身的边,这在邻接矩阵中表示为 graph[i][i]。在算法执行过程中,如果 graph[i][i] 的值更新为一个有意义的最小值(小于无穷大),它可能会影响到从该节点出发的路径成本计算。然而,通常在实现迪杰斯特拉算法时,会忽略自环,因为它们不会对从一个节点到另一个不同节点的最短路径计算产生影响。
🦆
在找到距离最小的未访问节点时,该算法是否考虑了效率优化,例如使用优先队列来优化查找过程?
在这个具体的算法实现中,并没有使用效率更高的数据结构如优先队列来优化查找过程。算法直接遍历所有节点来找到距离最小的未访问节点,这样的操作是 O(n) 的时间复杂度。在节点数量非常大的情况下,这种方法效率不是最优的。使用优先队列(特别是最小堆)可以将寻找最小距离节点的操作优化到 O(log n) 的时间复杂度,从而使整体算法的时间复杂度更低,特别是在稠密图中。然而,本题解中未采用这种优化可能是为了保持代码的简洁性或是出于教学目的。

相关问题