图最短路常用算法总结

Dijkstra算法

Dijkstra算法是一种单源最短路径算法,本质是贪心,只适用于没有负权边的图,并且不能用以计算最长路径

时间复杂度:?(?^2 )
优秀文章推荐:https://blog.csdn.net/qq_35644234/article/details/60870719

拓展:Dijkstra算法可以使用堆优化,优化后的效率是: 时间复杂度:?((?+?) log ?)(m为边数,n为顶点数) 堆优化的Dijkstra算法更适用于稀疏图

HihoCoder - 1081

#include <iostream>
#define INF 0x3f3f3f
#include <cstring>

using namespace std;

int N,M,S,T;

int graph[1005][1005];
bool vis[1005];
int dis[1005];

void Dijkstra()
{
    memset(vis,false,sizeof(vis));
   
    
    for(int j = 1; j <= N; j++ )
            dis[j] = graph[S][j];
            
    vis[S] = true;
    dis[S] = 0;        
    
    for(int i = 0; i < N - 1; i++ )//找n - 1次
    {
        int minn = INF;
        int u = S;//最小值的下标;
        
        
        
        for(int j = 1; j <= N; j++ )//找出最小值
        {
            if(!vis[j] && minn > dis[j])
            {
                u = j;
                minn = dis[j];
            }
        }
        
        if(minn == INF)
            break;
        
        vis[u] = true;//已更新
        
        for(int k = 1; k <= N; k++ )
            if(!vis[k] && minn + graph[u][k] < dis[k])
                dis[k] = minn + graph[u][k];
    }
    
}

int main()
{
    cin >> N >> M >> S >> T;
    memset(graph,INF,sizeof(graph));
    memset(dis,INF,sizeof(dis));
    
    for(int i = 0, x, y, z; i < M; i++ )
    {
        cin >> x >> y >> z;
        graph[x][y] = graph[y][x] = min(graph[x][y],z);
    }
    
    Dijkstra();
    
    cout << dis[T];
}

Bellman-Ford算法

Bellman-Ford算法 同样为单源路径算法,和Dijkstra算法不同的是, Bellman-Ford算法可以计算最长路径,还可以处理负权 边,本质是动态规划

时间复杂度:?(??)
优秀文章推荐: https://blog.csdn.net/sms0101/article/details/73088422
注:使用Bellman-Ford算法处理带负权边的图时,要记得判断是否存在负环

例题同上

#include <iostream>
//bell-man ford
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;

int n,m,s,t;

struct edge
{
    int from,to,len;
}edges[10005];

int dis[10005];

void Bell()
{
    dis[s] = 0;
    
    for(int i = 0; i < m; i++ )
        if(edges[i].from == s)
            dis[edges[i].to] = min(edges[i].len,dis[edges[i].to]);
        else if(edges[i].to == s)
            dis[edges[i].from] = min(dis[edges[i].from], edges[i].len);
    
    while(1)
    {
        bool ok = false;//检查是否有松弛
        for(int i = 0; i < m; i++ )
        {
            int u =edges[i].from;
            int v =edges[i].to;
            int l =edges[i].len;
            if(dis[v] > dis[u] + l)
                dis[v] = dis[u] + l, ok =true;
            else if(dis[u] > dis[v] + l)
                dis[u] = dis[v] + l, ok = true;
        }
        
        if(!ok)
            break;
    }
}

int main()
{
    cin >> n >> m >> s >> t;
    memset(dis,INF,sizeof(dis));
    for(int i = 0; i <m; i++ )
        cin >> edges[i].from >> edges[i].to >> edges[i].len;
    Bell();
    cout << dis[t];
}

Floyd算法

与前两者不同,Floyd算法用以计算图中所有两点间的最短或最长路径,本质也是动态规划,图的存储方式需为邻接矩阵

时间复杂度:?(?^3 )
优秀文章推荐: https://blog.csdn.net/qq_35644234/article/details/60875818
注:如果你喜欢的话,也可以对每个点都使用一次Dijkstra算法,但Floyd算法的代码可以只有仅仅4行

HihoCoder - 1089

#include <iostream>
#include <cstring>
#define INF 0x3f3f3f

using namespace std;

int n, m;

int graph[105][105];

void Floyd()
{
    for(int k = 1; k <= n; k++ )
        for(int i = 1; i <= n; i++ )
            for(int j = 1; j <=n; j++ )
                graph[i][j] = min(graph[i][j],graph[i][k] + graph[k][j]);
}

int main()
{
    cin >> n >> m;
    memset(graph,INF,sizeof(graph));
    
    for(int i = 0; i <= n; i++ )
        graph[i][i] = 0;
    
    for(int i = 0; i < m; i++ )
    {
        int x, y, z;
        cin >> x >> y >> z;
        graph[x][y] = graph[y][x] = min(z,graph[x][y]);
    }
    
    Floyd();
    
    for(int i = 1; i <= n; i++ )
    {
        for(int j = 1; j <= n; j++ )
            cout << graph[i][j] << " ";
        cout << endl;
    }
}

SPFA(Shortest Path Faster Algorithm)

SPFA其实是Bellman-Ford的队列优化版,所以适用情况与Bellman-Ford相同,平均效率比Bellman-Ford高

最坏情况时间复杂度: ?(??)
优秀文章推荐: https://blog.csdn.net/rentenglong2012/article/details/78483662
注:有些题目会针对SPFA,使SPFA遇到最坏情况,导致程序时间超限,所以选用SPFA时需要谨慎,可考虑用堆优化Dijk

HihoCoder - 1093

#include <iostream>
#define INF 0x3f3f3f3f
#include <cstring>
#include <queue>

using namespace std;

int n,m,s,t;

int dis[100005];
int head[100005];
bool vis[100005];

queue<int> q;

struct edge
{
    int from,to,len,next;
}edges[2000005];

void SPFA()
{
    memset(dis,INF,sizeof(dis));
    memset(vis,false,sizeof(vis));
    
    q.push(s);
    vis[s] = true;
    dis[s] = 0;
    
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;//
        for(int i = head[u]; i >0; i = edges[i].next )
        {
            int v = edges[i].to;
            int l = edges[i].len;
            if(dis[v] > dis[u] + l)
            {
                dis[v] = dis[u] + l;
                if(!vis[u])
                {
                    q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    
}

int main()
{
    cin >> n >> m >> s >> t;
    for(int i = 0, cnt = 0; i < m; i++ )
    {
        int u,v,l;
        cin >> u >> v >> l;
        edges[cnt] = {u,v,l,head[u]};
        head[u] = cnt++;
        
        edges[cnt] = {v,u,l,head[v]};
        head[v] = cnt++;
        
    }
    
    SPFA();
    
    cout << dis[t];
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值