最短路算法


单源最短路

固定出发点,求到达其它点的最短距离

(Dijkstra(迪杰斯特拉)算法

  • 不能解决负权边

堆优化的Dijkstra

#define MAX_N 100000 //最大点的数量

struct Node { //堆优化 
    int e, dis;  // dis表示点s到点e的距离 
    bool operator< (const Node& a) const {
        return this->dis > a.dis;
    }
};

struct edge { // 某个一条边上的点与b点的距离为v
    int b, v;
};

int ans[MAX_N + 5]; //ans[i] 表示i到起点s的最短距离 
vector<vector<edge> > ed(MAX_N + 5, vector<edge>()); //建立邻接表 

int s, n, m; //分别表示起点,点的个数,边的数量;
priority_queue <Node> q;
 
void Dijkstra() {
    memset(ans, 0x3f, sizeof(ans)); //初始化极大值 
    q.push((Node){s, 0});
    ans[s] = 0;
    while (!q.empty()) { //队列中存的是局部最优或者全局最优的点、距离 
        Node tp = q.top();
        q.pop();
        if (tp.dis > ans[tp.e]) continue; // 当前保留的距离是最短的,不能用tp.dis去更新其他点的最短距离 
        //更新其它点的最短距离 
        for (int i = 0; i < (int)ed[tp.e].size(); i++) {
            if (ans[ed[tp.e][i].b] <= tp.dis + ed[tp.e][i].v) continue;
            ans[ed[tp.e][i].b] = tp.dis + ed[tp.e][i].v;
            q.push((Node){ed[tp.e][i].b, ans[ed[tp.e][i].b]});
        }
    }
}

线段树优化的Dijkstra


斐波拉契堆优化的Dijkstra


#### Bellman-ford
  • 能解决负权边
小优化
struct edge {
    int s, e, v;
};

edge de[200005];
int n, m, s, ans[100005], edge_cnt;


#define min(a, b) ({\
    (a) > (b) ? (b) : (a);\
})

void Bellman_ford() { 
    memset(ans, 0x3f, sizeof(ans));
    ans[s] = 0;
    while (1) {
        int flag = 0; // 小优化
        for (int i = 0; i < edge_cnt; i++) {
            if (ans[de[i].e] > ans[de[i].s] + de[i].v) {
                ans[de[i].e] = ans[de[i].s] + de[i].v;
                flag = 1;
            }
            //ans[de[i].e] = min(ans[de[i].e], ans[de[i].s] + de[i].v);
        }
        if (flag == 0) break; //如果没有更新某两个点的最短距离,那么已经达到全局最优了 
    }
}
大优化
  • 基于队列的Bellman-ford(SPFA)
  • 由点s逐渐扩散,求得各个点到s的距离

bfs版的SPFA

#include <stdio.h>
 
struct edge {
    int e, v, next;
};


edge de[200005];
int n, m, s, ans[100005], edge_cnt;
int head[100005]; //链表头节点

#define min(a, b) ({\
    (a) > (b) ? (b) : (a);\
})
int q[100000]; //队列
int vis[100005];
void Bellman_ford() { 
    memset(ans, 0x3f, sizeof(ans));
    ans[s] = 0;
    q[0] = s;
    vis[s] = 1;
    int l = 0, r = 1;
    while (l < r) {
        int tp = q[l++];
        vis[tp] = 0;
        for (int i = head[tp]; i != -1; i = de[i].next) {
            if (ans[de[i].e] <= ans[tp] + de[i].v) continue;
            ans[de[i].e] = ans[tp] + de[i].v;
            if (vis[de[i].e]) continue;
            q[r++] = de[i].e;
            vis[de[i].e] = 1;
        }
    }
}

void add_edge(int a, int b, int c) {
    de[edge_cnt].e = b;
    de[edge_cnt].v = c;
    de[edge_cnt].next = head[a];
    head[a] = edge_cnt++;
}

int main() {
    memset(head, -1, sizeof(head));
    scanf("%d %d %d", &n, &m, &s);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        add_edge(a, b, c);
        add_edge(b, a, c);
    }
    Bellman_ford();
    for (int i = 1; i <= n; i++) {
        printf("%d\n", ans[i] == 0x3f3f3f3f ? -1 : ans[i]);
    }
    return 0;
}

dfs版的SPFA


多源最短路

求任意两点最短距离

Floyd

动态规划思想

#define min(a, b) ({\
    (a) > (b) ? (b) : (a);\
})
void Floyd() {
    //n表示点的个数, 点的编号从 1 开始 
    for (int k = 1; k <= n; k++) { // 中转站 
        for (int l = 1; l <= n; l++) {
            for (int r = 1; r <= n; r++) {
                dis[l][r] = min(dis[l][r], dis[l][k] + dis[k][r]);
            }
        }
    }
}

总结

算法名称 图存储方式 适合情况 时间复杂度 说明
Floyd 矩阵 多源 O(n^3) 可以存在负权边,图中不能存在环,能解决最长路径问题
Dijkstra 邻接表,链式前向星 单源 O(n ^ n) 不能存在负权边,不能存在负权环
优先队列优化的Dijksra 邻接表,链式前向星 单源 O(m * logn),常数大 不能存在负权边,不能存在负权环
线段数优化的Dijksra 邻接表,链式前向星 单源 O(m * logn),常数小 不能存在负权边,不能存在负权环
BEllman-ford 邻接表,链式前向星 单源 O(n * m) 能判断负环,不能解决负环最短路,边权可以为负,能解决最长路径问题
SPFA 邻接表,链式前向星 单源 O(n * m) 到 O(Km) 能判断负环,不能解决负环最短路,边权可以为负,能解决最长路径问题
斐波拉契堆优化的Dijkstra 邻接表,链式前向星 单源 O(m + nlogn) 不能存在负权边,不能存在负权环

文章作者: Axieyun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Axieyun !
评论
评论
  目录