//点击后出现烟花效果

最短路算法

Dijkstra朴素 O(n2)



用途:

求解非负权图中单源最短路问题


思路:

维护一个已确定最短路径的顶点集合 S 和一个存储源点到各个顶点的最短距离的数组 dist

初始时, S 只包含源点,dist 中只有源点到自身的距离为 0 ,其他为无穷大。采用贪心策略,每次从未确定最短路径的顶点集合中选择距离源点最近的一个顶点,然后以该顶点为中介更新其他顶点的距离。

重复以下步骤直到 S 包含所有顶点:

  • dist 中选出不在 S 中且距离最小的顶点 u,将 u 加入 S
  • 遍历 u 的所有邻接顶点 v ,如果dist[u] + w(u,v) < dist[v],则更新dist[v] = dist[u] + w(u,v),其中 w(u,v) 是边 (u,v) 的权值。

代码:

int g[N][N], dist[N];
bool st[N];

int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1] = 0;
    for (int i = 0;i < n;i++)
    {
        int t = n + 1; //这样做是让dist[t]初始为无穷,方便找到要加入集合的那个点
        for (int j = 1;j <= n;j++)
            if (!st[j] && dist[t] > dist[j]) //找到在所有未访问过的节点中,dist值最小的节点t。这个节点是下一个要访问的节点。
                t = j;
        st[t] = 1;
        if (t == n) break; //如果t等于n,那么说明从1到n的最短路径已经找到了,循环可以结束
        for (int j = 1;j <= n;j++) 
            dist[j] = min(dist[j], dist[t] + g[t][j]); //更新所有未访问过的节点j的dist值
    }
    return dist[n];
}




Dijkstra堆优化 O(mlogn)


思路

Dijkstra 算法的堆优化方法是利用堆(或优先队列)来存储和更新每个节点到源点的最短距离,从而减少寻找最小距离节点的时间复杂度。

  • 将未访问的节点放入一个堆中(通常使用小根堆)
  • 选取堆顶元素作为当前操作的节点,将其从堆中移除
  • 对于当前节点的每一个邻居节点,如果新的路径长度比原先的更短,就更新邻居节点的距离,然后将它插入堆中

代码

int e[N], ne[N], idx, h[N], w[N];
int n,m;
int dist[N];
bool st[N];

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0,1});
    while (!q.empty())
    {
        auto t = q.top();
        q.pop();
        int v = t.second;
        if (st[v]) continue; //已经在集合中就直接跳过
        st[v] = 1; //此时加入集合
        for (int i=h[v];~i;i=ne[i]) //邻接表遍历相邻结点
        {
            int j = e[i];
            if (dist[j] > dist[v] + w[i])
            {
                dist[j] = dist[v] + w[i]; //松弛
                q.push({dist[j], j}); //加入堆
            }
        }
    }
    return dist[n];
}




bellman-ford O(mn)


用途

解决存在负权边的单源最短路径问题


基本思路

不断地松弛每条边来逐步缩小起点到各个节点的距离

给定一个加权有向图 G=(V,E) ,其中 V 表示顶点集合,E表示边集合,边 e 的权重为 w(e) ,起点为 s ,终点为 t ,则 BellmanFord 算法的流程如下:

  1. 对于每个节点 iV ,初始化距离数组 distance[i] 为正无穷大,除了初始节点 s ,这里将 distance[s] 设置为 0

  2. 对于所有的节点 uv ,如果存在一条从 uv 的边 e,更新distance[v] = min(distance[v], distance[u] + w(e))

  3. 重复执行第2步,直到没有任何一条边可以使得 distance 的值发生变化。

  4. 根据 distance[t] 判断是否存在从 st 的路径,如果存在输出该路径,否则说明不存在从 st 的路径。


代码

struct
{
    int a,b,w;
}edge[M];
int n,m,k;
int dist[N], backup[N];

int bellman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1] = 0;
    for (int i=0;i<k;i++) //k是拓展的层数,如果要全图的距离,遍历n次即可
    {
        memcpy(backup, dist, sizeof dist); //每次只从上一层拓展,不然k没意义
        for (int j=0;j<m;j++)
        {
            int a = edge[j].a, b = edge[j].b, w = edge[j].w;
            dist[b] = min(dist[b], backup[a] + w);
        }
    }
    return dist[n];
}




spfa O(m) - O(mn)


用途:

  • 解决存在负权边的单源最短路径问题,是Bellman-Ford算法的一种优化版本
  • 判断是否存在负环

基本思路

将起点放入队列中,然后不断从队列中取出节点,遍历其所有邻居节点,并尝试使用该节点更新邻居节点的距离值。如果某个邻居节点的距离值发生了变化,则将其加入队列中。这个过程会重复进行多次,直到队列为空为止。


代码

求最短路:

int e[N], ne[N], idx, h[N], w[N];
int dist[N];
int n,m;
bool st[N];
int q[N], tt,hh;

int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    q[tt++] = 1;
    st[1] = 1;
    while (hh <= tt)
    {
        int t = q[hh++];
        st[t] = 0;
        for (int i=h[t];~i;i=ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q[tt++] = j;
                    st[j] = 1;
                }
            }
        }
    }
    return dist[n];
}



判断负环:

int e[N], ne[N], idx, w[N], h[N];
int dist[N], q[N], tt , hh;
int n,m;
bool st[N];
int cnt[N];

bool spfa()
{
    memset(dist, 0x3f, sizeof dist);
    for (int i=1;i<=n;i++)
    {
        q[tt++] = i;
        st[i] = 1;
    }
    while (hh <= tt)
    {
        int t = q[hh++];
        st[t] = 0;
        for (int i=h[t];~i;i=ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q[tt++] = j;
                    st[j] = 1;
                    cnt[j] = cnt[t] + 1; //从源点到j经过的边数+1
                }
                if (cnt[j] >= n) return true; //边数超过n就代表存在负环
            }
        }
    }
    return false;
}




Floyd O(n3)


用途

解决任意两点间最短路径问题


基本思路

动态规划

给定一个加权有向图 G=(V,E),其中 V 表示顶点集合, E 表示边集合,边 e 的权重为 w(e) ,则 Floyd 算法的基本思路如下:

  • 对于任意两个节点 ij ,初始化它们之间的距离 d[i][j]ij 的直接距离,如果不存在直接边,则距离为正无穷大。

  • 对于每个节点 kV ,尝试使用 k 节点作为中介点来缩短 ij 的距离,即d[i][j] = min(d[i][j], d[i][k] + d[k][j])

重复执行第2步,直到所有节点之间的距离都被计算出来。


代码

int d[N][N];

int floyd()
{
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}




posted @   JunieXD  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示