优先队列启发式最短路径快速算法(优化SPFA)-HEAP_SPFA算法

简介

优先队列启发式最短路径快速算法(Heap‑SPFA)是基于 SPFA 范式改良的启发式实验性最短路算法,不具备全新理论突破,仅用于算法学习与探索。

其核心思想:沿用 SPFA 的松弛框架,将普通 FIFO 队列替换为小根优先队列,优先松弛当前预估距离更小的节点,期望减少无效松弛迭代;保留入堆次数判定源点可达负环的逻辑。

重要声明

  1. 本算法非标准工业 / 竞赛可用算法,仅作学习研究;
  2. 无负权图下行为等价堆优化 Dijkstra,但实现范式不同;
  3. 含负权图可求解最短路、可检测负环,但负环检测灵敏度低于标准队列 SPFA;
  4. 最坏时间复杂度仍为 \(O(nm)\),无理论复杂度优化;
  5. 优先队列存在常数开销,部分场景性能不如标准 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;
}

使用说明

  1. 输入格式:第一行 n m s(点数、边数、源点),后续 mu v w 表示有向边 \(u\to v\),权值 w;
  2. 输出:存在源点可达负环则输出 Negative cycle exists;否则依次输出每个点的最短路,不可达输出 INF
  3. 数据范围适配:使用 long long 存储距离,避免溢出。

算法选型提醒

  • 无负权图:优先使用堆优化 Dijkstra;
  • 带负权且需要灵敏判负环:优先使用标准队列版 SPFA;
  • Heap‑SPFA 仅作学习探索,不用于正式工程与竞赛。

^_^

相关推荐
SiliconGazer1 小时前
第15届国赛满分代码解析(下)—— 运动轨迹算法、按键交互与完整状态机
算法·状态机·stc15f2k60s2·浮点运算·蓝桥杯国赛·运动轨迹、·向量分解
Navigator_Z1 小时前
LeetCode //C - 1096. Brace Expansion II
c语言·算法·leetcode
luj_17681 小时前
FreeDOS vs MS-DOS PC-DOS 对比解析
服务器·c语言·开发语言·经验分享·算法
笨笨没好名字2 小时前
Leetcode刷题python版第一周
python·算法·leetcode
Cthy_hy2 小时前
斯特林数:组合划分的递归经典,一二两类全解
python·算法·斯特林数
不忘不弃2 小时前
计算pi的近似值
算法
码云骑士2 小时前
12-GIL不是性能杀手(下)-绕过GIL的三种方案与决策树
算法·决策树·机器学习
一只齐刘海的猫2 小时前
【Leetcode】无重复字符的最长子串
算法·leetcode·职场和发展
行智科技2 小时前
FAST-LIVO2 源码精读(二):环境搭建与编译避坑
算法·ubuntu·自动驾驶·slam