There are N
network nodes, labelled 1
to N
.
Given times
, a list of travel times as directed edges times[i] = (u, v, w)
, where u
is the source node, v
is the target node, and w
is the time it takes for a signal to travel from source to target.
Now, we send a signal from a certain node K
. How long will it take for all nodes to receive the signal? If it is impossible, return -1
.
Example 1:
Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2
Output: 2
Note:
N
will be in the range[1, 100]
.K
will be in the range[1, N]
.- The length of
times
will be in the range[1, 6000]
. - All edges
times[i] = (u, v, w)
will have1 <= u, v <= N
and0 <= w <= 100
.
class Solution { public int networkDelayTime(int[][] times, int N, int K) { double[][] disTo = new double[N][N]; for (int i = 0; i < N; i++) { Arrays.fill(disTo[i], Double.POSITIVE_INFINITY); } for (int i = 0; i < N; i++) { disTo[i][i] = 0; } for (int[] edge: times) { disTo[edge[0] - 1][edge[1] - 1] = edge[2]; } for (int k = 0; k < N; k++) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (disTo[i][j] > disTo[i][k] + disTo[k][j]) disTo[i][j] = disTo[i][k] + disTo[k][j]; } } } double max = Double.MIN_VALUE; for (int i = 0; i < N; i++) { if (disTo[K - 1][i] == Double.POSITIVE_INFINITY) return -1;//如果有一个节点从k到不了,就说明无法完成任务返回-1 max = Math.max(max, disTo[K - 1][i]); } return (int) max; } }
小改一下
class Solution { public int networkDelayTime(int[][] times, int N, int K) { int[][] disTo = new int[N][N]; for (int i = 0; i < N; i++) { Arrays.fill(disTo[i], 100000); } for (int i = 0; i < N; i++) { disTo[i][i] = 0; } for (int[] edge: times) { disTo[edge[0] - 1][edge[1] - 1] = edge[2]; } for (int k = 0; k < N; k++) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (disTo[i][j] > disTo[i][k] + disTo[k][j]) disTo[i][j] = disTo[i][k] + disTo[k][j]; } } } int max = -100000; for (int i = 0; i < N; i++) { if (disTo[K - 1][i] == 100000) return -1; max = Math.max(max, disTo[K - 1][i]); } return max; } }
1. floyd-warshall
初始化的时候i==j置0,表示没有走动
先把最终矩阵求出来,意思是从i到j的最短路径,
这道题最终转化成从节点k开始,最多要花多少步才能传播到所有节点,所以是上面的矩阵中取大值。
比如从2出发,到1要一步,到4要二步,答案就是2.
class Solution { public int networkDelayTime(int[][] times, int N, int K) { int[] dist = new int[N + 1]; Arrays.fill(dist, Integer.MAX_VALUE); dist[K] = 0; for(int i = 1; i < N; i++) { // relax edges repeatedly, connecting relaxed source nodes int[] temp= Arrays.copyOf(dist,N+1); for(int[] e : times) { // traverse edge list int u = e[0], v = e[1], w = e[2]; if (dist[u] != Integer.MAX_VALUE && dist[v] > temp[u] + w) { // connect newest reachable source temp[v] = dist[u] + w; } } dist = temp; } int maxwait = 0; for(int i=1; i <= N; i++) maxwait = Math.max(maxwait, dist[i]); return maxwait == Integer.MAX_VALUE ? -1 : maxwait; } }
1
2. Bellman Ford
初始化dist[k] == 0, 其余为Integer.MAX_VALUE,转移方程是dist[v] = min[dist[v], dist[u] + w(u, v)]
有N个节点外循环就有N-1次。
意义:初始时除了自己外所有的dist【】置最大值,然后总共松弛N-1次,每一次遍历所有edge,
if (dist[u] != Integer.MAX_VALUE && dist[v] > dist[u] + w)
这个前面不等于极大值是要从出发点开始判断的意思,如果是极大值就说明这个点还到不了,就更没有办法经过uv到下一个点了。
整个算法一直在维护和update一个dist【】数组,意思是从指定点到另外一个点v的距离,dist[u]是当前能到达的点, dist[v]是edge UV的终点,如果dist[v]大,就更新成指定点经过uv到达v的值
https://www.youtube.com/watch?v=obWXjtg0L64
3. Dijistra algorithm
class Solution { public int networkDelayTime(int[][] times, int N, int K) { Map<Integer, Map<Integer,Integer>> map = new HashMap<>();//from, <to, distance> for(int[] time : times){ map.putIfAbsent(time[0], new HashMap<>()); map.get(time[0]).put(time[1], time[2]); } //离K的距离,节点 Queue<int[]> pq = new PriorityQueue<>((a,b) -> (a[0] - b[0]));//用pq是为了每次获得和K距离最短的节点 pq.offer(new int[]{0, K}); boolean[] visited = new boolean[N+1];//每次把没有松弛的节点拿出来,松弛后更新 int res = 0; while(!pq.isEmpty()){ int[] cur = pq.poll(); int curNode = cur[1]; int curDist = cur[0]; if(visited[curNode]) continue; visited[curNode] = true; res = curDist; N--; if(N == 0) return res;//所有节点都考虑完了 if(map.containsKey(curNode)){//因为是单向图所以先考虑一下存不存在 for(int next : map.get(curNode).keySet()){ pq.add(new int[]{curDist + map.get(curNode).get(next), next}); } } } return N == 0 ? res : -1; } }
算法的基本思想是:每次找到离源点(上面例子的源点就是 1 号顶点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。基本步骤如下:
- 将所有的顶点分为两部分:已知最短路程的顶点集合 P 和未知最短路径的顶点集合 Q。最开始,已知最短路径的顶点集合 P 中只有源点一个顶点。我们这里用一个 book[ i ]数组来记录哪些点在集合 P 中。例如对于某个顶点 i,如果 book[ i ]为 1 则表示这个顶点在集合 P 中,如果 book[ i ]为 0 则表示这个顶点在集合 Q 中。
- 设置源点 s 到自己的最短路径为 0 即 dis=0。若存在源点有能直接到达的顶点 i,则把 dis[ i ]设为 e[s][ i ]。同时把所有其它(源点不能直接到达的)顶点的最短路径为设为 ∞。
- 在集合 Q 的所有顶点中选择一个离源点 s 最近的顶点 u(即 dis[u]最小)加入到集合 P。并考察所有以点 u 为起点的边,对每一条边进行松弛操作。例如存在一条从 u 到 v 的边,那么可以通过将边 u->v 添加到尾部来拓展一条从 s 到 v 的路径,这条路径的长度是 dis[u]+e[u][v]。如果这个值比目前已知的 dis[v]的值要小,我们可以用新值来替代当前 dis[v]中的值。
- 重复第 3 步,如果集合 Q 为空,算法结束。最终 dis 数组中的值就是源点到所有顶点的最短路径。
以上摘自https://wiki.jikexueyuan.com/project/easy-learn-algorithm/dijkstra.html