1. 程式人生 > >[Week 4] LeetCode 743. Network Delay Time

[Week 4] LeetCode 743. Network Delay Time

LeetCode 743. Network Delay Time

問題描述:

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.

Note:

  1. N will be in the range [1, 100].
  2. K will be in the range [1, N].
  3. The length of times will be in the range [1, 6000].
  4. All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 1 <= w <= 100.

題解:

很容易看出這是一個單源最短路徑問題,只需要求出從原點K到圖中其它點的最短路徑,然後找到其中最長的即為題解,當然結果出現infinite意味著該圖不連通,返回-1即可。

這類問題的求解,我們需要知道一個資訊:是否會有負權邊出現?OK,這道題邊權代表時間,所以不會出現。對於無負邊權的單源最短路徑問題,我們很容易想到Dijkstra演算法,那什麼是Dijkstra演算法呢?

Dijkstra 演算法:

給定一個有向圖G(V,E),我們一開始初始化一個dist陣列(dist[i]指源點A到頂點i的距離),除了源點為0,其它頂點設定為infinite

  1. 我們從源點A開始,找到與源點相連的頂點,通過遍歷以A
    為起點的邊,更新dist陣列,即設定源點到其直接相連點的距離。 在這裡插入圖片描述
  2. 然後找到dist數組裡除 源點Adist[i]最小的頂點:C,通過遍歷以C為起點的邊,更新dist陣列。 在這裡插入圖片描述
  3. 接著找到dist 數組裡除 源點A 和 C 外dist[i]最小的頂點:B,通過遍歷以B為起點的邊,更新dist`陣列。 在這裡插入圖片描述
  4. 後面DE交給讀者自己分析,看看最後的結果: 在這裡插入圖片描述
# 虛擬碼
Input:G(V, E) -- a directed graph with vertices collection V and edges collection E
Output: For all vertices u reachable from source s, dist(u) is the distance from s to u

for all u in V:
	dist(u) = infinite
dist(s) = 0

# 使用優先佇列找到值最小的點
Q = makequeue(V) (use dist as keys)
while Q is not empty:
	u = Q.pop()
	for all e(u,v) in E:
		if dist(v) > dist(u) + l(u,v):
			dist(v) = dist(u) + l(u,v)
			decreaseKey(Q,v)  # 讓佇列中元素重新排列

程式碼:

#include <vector>
#include <queue>
#include <climits>

using namespace std;

typedef pair<int, int> node;

int networkDelayTime(vector<vector<int> > &times, int N, int K)
{
  /* 
   * initialize the graph :
   * nodes are labelled 1 to N, so we let index start from 1 not 0 
   */
  vector<vector<int> > graph(N + 1, vector<int>(N + 1, -1));
  for (int i = 0; i < times.size(); ++i)
  {
    vector<int> &e = times[i];
    graph[e[0]][e[1]] = e[2];
  }

  /*
   * dist[i] means distance of (K, i), INT_MAX means disconnected
   * visited[i] means node has been closed or not 
   */
  vector<int> dist(N + 1, INT_MAX);
  vector<bool> visited(N + 1, false);
  dist[K] = 0;

  priority_queue<node, vector<node>, greater<node> > que;
  que.push(node(dist[K], K));
  while (!que.empty())
  {
    node u = que.top();
    que.pop();
    int u_num = u.second;
    if (visited[u_num])
      continue;

    for (int i = 1; i < graph[u_num].size(); ++i)
    {
      if (graph[u_num][i] != -1 && dist[i] > dist[u_num] + graph[u_num][i])
      {
        dist[i] = dist[u_num] + graph[u_num][i];
        if (!visited[i])
          que.push(node(dist[i], i));
      }
    }
  }

  int result = 0;
  for (int i = 1; i <= N; ++i)
  {
    if (dist[i] == INT_MAX)
      return -1;
    else
      result = max(result, dist[i]);
  }
  return result;
}

複雜度分析:

首先考慮一下不使用優先佇列的情況,每次我們都需要找到dist陣列中值最小的點,複雜度為O(|V|);然後遍歷以該點為起點的邊,所以演算法結束會遍歷所有的邊,即O(|E|);總共需要遍歷|V|次,所以總時間複雜度為 O(|V|^2)

使用優先佇列的情況,考慮應用二分堆實現的優先佇列。取出dist中最小值點,因為刪除節點都會導致堆的結構調整,複雜度為O(log|V|);遍歷每條邊時,因為dist[i]值的變化,需要調整堆的結構,複雜度為O(log|V|);總的來說,大概需要|V|次取最小值點,|E|次改變 dist[i]的值,所以總複雜度為 O((|V|+|E|)log(|V|))