【迪杰斯特拉】LeetCode 743. 网络延迟时间
题目
https://leetcode.cn/problems/network-delay-time/description/
题解
这是典型的单源最短路径模板题,思路是使用迪杰斯特拉算法算出从源点到所有点的最短路径的最大值,若存在无法抵达的目标点,则答案输出 \(-1\)。
参考代码
// 最短路径策略
struct ShortestPath {
using ll = long long;
static constexpr ll INF = 1e18;
static constexpr ll INIT_VALUE = 0LL;
static bool better(ll newDist, ll oldDist) {
return newDist < oldDist; // 更短更好
}
using Queue = priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<>>;
};
// 最长路径策略
struct LongestPath {
using ll = long long;
static constexpr ll INF = -1e18; // 负无穷
static constexpr ll INIT_VALUE = 0LL;
static bool better(ll newDist, ll oldDist) {
return newDist > oldDist; // 更长更好
}
using Queue = priority_queue<pair<ll, int>>; // 大顶堆
};
/**
* @brief Dijkstra 算法模板类
*
* 通过策略模式支持最短路径和最长路径计算。
* 默认使用 ShortestPath 策略求最短路径。
*
* @tparam Policy 策略类,定义路径长度的比较方式
* - ShortestPath: 求最短路径(适用于所有非负权图)
* - LongestPath: 求最长路径(仅适用于 DAG)
*
* @example
* @code
* // 1. 构建邻接表
* // 共 n 个节点,节点编号从 0 开始邻接表大小就开 n,从 1 开始就开 n+1
* vector<vector<array<long long, 2>>> adj(n + 1);
*
* // 2. 添加边
* // vv 是边的集合,每条边格式为 {起点 ui, 终点 vi, 权重 wi}
* // 具体创建逻辑需根据实际数据格式分析
* for (auto &v : vv) {
* adj[v[0]].push_back({v[1], v[2]});
* }
*
* // 3. 执行算法
* // 使用最短路径策略(默认),计算从起点 k 到所有节点的最短距离
* Dijkstra dijk(adj, k);
*
* // 使用最长路径策略(仅适用于 DAG),计算从起点 k 到所有节点的最长距离
* Dijkstra<LongestPath> dijk(adj, k);
*
* // 4. 查询结果
* bool available = dijk.available(destination);
* // - true: 终点 destination 可达
* // - false: 终点 destination 不可达
*
* long long distance = dijk.getDistance(destination);
* // - 返回到达终点 destination 的最优距离
* // - 若不可达,返回 INF(ShortestPath 为 1e18,LongestPath 为 -1e18)
* @endcode
*
* @note
* - 最短路径要求所有边权非负
* - 最长路径仅适用于有向无环图(DAG)
* - 节点编号需统一(从 0 或 1 开始),邻接表大小需匹配
*/
template<typename Policy = ShortestPath>
class Dijkstra {
private:
using ll = long long;
const ll INF = Policy::INF;// 不可达标记值
const ll INIT_VALUE = Policy::INIT_VALUE;// 源点初始距离
const char TRUE = '1', FALSE = '0';// TRUE 代表访问过, FALSE 代表未访问
vector<char> vis;// vis[i] 表示节点 i 的访问信息,值为 TRUE/FALSE
vector<ll> dist;// dist[i] 表示从源点到节点 i 的最优距离(最优的定义取决于 Policy::better)
vector<int> path;// path[i] 表示从源点到节点 i 的最优路径的倒数第二个节点编号,若节点 i 没有上一个节点,值为 INF
public:
/**
* @brief 构造函数:执行 Dijkstra 算法,计算从源点 source 到所有节点的最优路径
* @param adjacencyList 邻接表,adjacencyList[u] 存储从节点 u 出发的所有边
* 每条边用 array<ll, 2> 表示:{目标节点, 边的权重}
* @param source 源点编号(信号发出的起始节点)
*/
Dijkstra(vector<vector<array<ll, 2>>>& adjacencyList, int source) {
int n = adjacencyList.size();// 邻接表大小
// 初始化维护的信息时,数组大小要开邻接表大小 + 1,因为节点有可能从 1 开始编号
vis.assign(n + 1, FALSE);// 初始化访问标记数组,全部标记为未访问
dist.assign(n + 1, INF);// 初始化距离数组,全部标记为不可达
dist[source] = INIT_VALUE;// 源点到自身的距离设为初始值
path.assign(n + 1, INF);// 初始化路径数组,以节点 i 为终点的最优路径的倒数第二个节点全部标记为不可达
typename Policy::Queue pq;// // 优先队列(first 维护距离,second 维护点信息),根据策略自动选择小顶堆或大顶堆
pq.emplace(INIT_VALUE, source);// 将源点加入队列:{距离, 节点编号}
while (!pq.empty()) {
auto [dis, u] = pq.top();// 获取当前距离最优的节点
pq.pop();// 从队列中移除
if (vis[u] == TRUE) {// 如果节点 u 已经被处理过,跳过(避免重复处理)
continue;
}
vis[u] = TRUE;// 标记节点 u 为已处理
// adjacencyList[u] 中的每个元素是 {v, distance},表示 u -> v 边权为 distance
for (auto &[v, distance]: adjacencyList[u]) {
ll newDist = distance + dist[u];// 从 source 到 u,再从 u 到 v 的距离
if (Policy::better(newDist, dist[v])) {// 比原本从 source 到 v 的路径更优
path[v] = u;// 更新从 source 到 v 的最优路径的倒数第二个节点编号
dist[v] = newDist;// 更新从 source 到 v 的最优路径长度
pq.emplace(newDist, v);// 将节点 v 的新距离加入优先队列
}
}
}
}
/**
* @brief 获取从源点到目标点的最优路径距离
* @param destination 目标节点编号
* @return 最优路径长度(最短或最长,取决于策略)
* 如果不可达,返回 INF
*/
ll getDistance(int destination) {
return dist[destination];
}
/**
* @brief 判断从源点到目标点是否存在至少一条路径
* @param destination 目标节点编号
* @return true 如果可达,false 如果不可达
*/
bool available(int destination) {
return dist[destination] != INF;
}
int getPreviousNode(int currentNode) {
return path[currentNode];
}
bool hasPreviousNode(int currentNode) {
return available(getPreviousNode(currentNode));
}
/**
* @brief 获取从源点到目标点的最优路径
* @param destination 目标节点编号
* @return 最优路径,如果不可达,返回空
*/
stack<int> getPath(int destination) {
stack<int> stk;// 用栈存储从目标点到源点的路径
if (available(destination)) {// 从源点 source 到目标点 destination 是否存在至少一条路径
while (destination != INF) {// 节点可达
stk.emplace(destination);// 加入路径
destination = getPreviousNode[destination];// 获取当前节点的上一个节点
}
}
return stk;
}
};
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int n, int k) {
// 构建邻接表:大小为 n+1,因为节点编号从 1 开始(0 号位置不使用)
vector<vector<array<long long, 2>>> adj(n + 1);
// 遍历所有边,将其加入邻接表
// times[i][0] = 起点 ui
// times[i][1] = 终点 vi
// times[i][2] = 权重 wi
for (auto &v: times) {
adj[v[0]].push_back({v[1], v[2]});
}
// 使用最短路径策略执行 Dijkstra 算法,计算从起点 k 到所有节点的最短距离
Dijkstra dijk(adj, k);
int ans = 0;
for (int i = 1; i <= n; ++ i) {// 遍历所有节点(从 1 到 n)
if (dijk.available(i)) {// 节点 i 可达
ans = max(ans, (int)dijk.getDistance(i));// 更新答案为所有最短距离中的最大值
} else {// 存在不可达节点,信号无法传递到所有节点
ans = -1;
break;
}
}
return ans;
}
};
浙公网安备 33010602011771号