简介
优先队列启发式最短路径快速算法(Heap‑SPFA)是基于 SPFA 范式改良的启发式实验性最短路算法,不具备全新理论突破,仅用于算法学习与探索。
其核心思想:沿用 SPFA 的松弛框架,将普通 FIFO 队列替换为小根优先队列,优先松弛当前预估距离更小的节点,期望减少无效松弛迭代;保留入堆次数判定源点可达负环的逻辑。
重要声明
- 本算法非标准工业 / 竞赛可用算法,仅作学习研究;
- 无负权图下行为等价堆优化 Dijkstra,但实现范式不同;
- 含负权图可求解最短路、可检测负环,但负环检测灵敏度低于标准队列 SPFA;
- 最坏时间复杂度仍为 \(O(nm)\),无理论复杂度优化;
- 优先队列存在常数开销,部分场景性能不如标准 SPFA。
C++ 完整实现
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
struct Edge {
int to;
ll w;
};
vector<vector<Edge>> g;
vector<ll> dis;
vector<int> in_cnt; // 入堆次数,用于判源点可达负环
vector<bool> in_heap;
int n, m;
bool has_neg_cycle = false;
void HeapSPFA(int s) {
dis.assign(n + 1, INF);
in_cnt.assign(n + 1, 0);
in_heap.assign(n + 1, false);
dis[s] = 0;
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<>> hp;
hp.emplace(0, s);
in_heap[s] = true;
in_cnt[s]++;
while (!hp.empty() && !has_neg_cycle) {
auto [d, u] = hp.top();
hp.pop();
in_heap[u] = false;
// 跳过过期松弛项
if (d > dis[u]) continue;
for (auto& e : g[u]) {
int v = e.to;
ll w = e.w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!in_heap[v]) {
hp.emplace(dis[v], v);
in_heap[v] = true;
in_cnt[v]++;
// 入堆次数>=n 说明存在源点可达负权回路
if (in_cnt[v] >= n) {
has_neg_cycle = true;
return;
}
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); // 兼容 C++ 旧标准
int s;
cin >> n >> m >> s;
g.resize(n + 1);
for (int i = 0; i < m; i++) {
int u, v; ll w;
cin >> u >> v >> w;
g[u].push_back({v, w});
}
HeapSPFA(s);
if (has_neg_cycle) {
cout << "Negative cycle exists\n";
} else {
for (int i = 1; i <= n; i++) {
if (dis[i] == INF) cout << "INF ";
else cout << dis[i] << " ";
}
}
return 0;
}
使用说明
- 输入格式:第一行
n m s(点数、边数、源点),后续m行u v w表示有向边 \(u\to v\),权值 w; - 输出:存在源点可达负环则输出
Negative cycle exists;否则依次输出每个点的最短路,不可达输出INF; - 数据范围适配:使用
long long存储距离,避免溢出。
算法选型提醒
- 无负权图:优先使用堆优化 Dijkstra;
- 带负权且需要灵敏判负环:优先使用标准队列版 SPFA;
- Heap‑SPFA 仅作学习探索,不用于正式工程与竞赛。
^_^