图论算法及其模板

13 篇文章 0 订阅
10 篇文章 0 订阅

什么方法好,和数据关系很大,和你使用的数据结构也有关。比如使用vector,它自身就有复杂度,所以在稠密图上不及二维数组的邻接矩阵,但在稀疏图方面肯定vector的动态数组实现的链接列表更好。有些复杂度高的算法,其实际体验可能比复杂度低的更快,因为复杂度是按最糟糕的情况算的,而且复杂度低的算法往往是使用数据结构优化而实现的,而实现数据结构也是存在复杂度的,所以会出现这些个情况。

最短路

最短路算法有很多,具体哪个好,和数据是有很大关系的

Bellman-Ford

从起点开始向外扩展,最坏O(|V|*|E|),实际体验比O(|E|log|V|) 的Dijkstra可能快

#include<stdio.h>
#include<algorithm>
#define MAX_E 4002
#define MAX_V 1002
#define INF 0x3f3f3f
using namespace std;
struct edge{int from,to,cost;};
edge es[MAX_E];
int d[MAX_V],V,E;

void bellman_ford(int s){
    fill(d,d+V+1,INF);
    d[s]=0;
    while(true){
        bool update=false;
        for(int i=0;i<E;i++){
            edge e=es[i];
            if(d[e.from]!=INF&&d[e.from]+e.cost<d[e.to]){
                d[e.to]=d[e.from]+e.cost;
                update=true;
            }
        }
        if(!update) break;
    }
}

spfa

将上面的算法用堆优化后就是spfa了,该算法是不稳定的,但大部分情况下该算法是最快的

#include<stdio.h>
#include<queue>
#include<algorithm>
#define MAX_E 4002
#define MAX_V 1002
#define INF 0x3f3f3f
using namespace std;
struct edge{int from,to,cost;};
edge es[MAX_E];
int d[MAX_V],V,E;

void bellman_ford(int s){
    fill(d,d+V+1,INF);
    d[s]=0;
    queue<int> que;
    que.push(s);
    while(!que.empty()){
        int t=que.front();que.pop();
        edge e=es[t];
        if(d[e.from]+e.cost<d[e.to]){
            d[e.to]=d[e.from]+e.cost;
            que.push(e.to);
        }
    }
}

Dijkstra

和上面两个算法的原理差不多,只是使用了优先队列实现,以减少冗余操作,复杂度O(|E|log|V|)

#include<stdio.h>
#include<queue>
#include<vector>
#define INF 0x3f3f3f
#define MAX_N 1002
using namespace std;

struct edge{int to,cost;}E;
typedef pair<int ,int> P;//f 距离 s 顶点

int V,T;
int d[MAX_N];

bool operator <(P x,P y){
    return x.first>y.first;
}

void dijkstra(int s,vector<edge> *G){
    //vector<edge> G[V+1];
    priority_queue<P> que;
    fill(d,d+V+1,INF);
    d[s]=0;
    que.push(P(0,s));

    while(!que.empty()){
        P p=que.top();que.pop();
        int v=p.second;
        if(d[v]<p.first) continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[v]+e.cost<d[e.to]){
                d[e.to]=d[v]+e.cost;
                que.push(P(d[e.to],e.to));
            }
        }
    }
}

补充

次短路算法

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
#define MAX_N 1005
using namespace std;
struct edge{int to,cost;};
typedef pair<int,int> P;
int dist[MAX_N];    //最短路
int dist2[MAX_N];   //次短路
int N;
vector<edge> G[MAX_N];

void solve(){
    priority_queue<P,vector<P>,greater<P> > que;
    fill(dist,dist+N,INF);
    fill(dist2,dist2+N,INF);
    dist[0]=0;
    que.push(P(0,0));
    while(!que.empty()){
        P p=que.top();que.pop();
        int v=p.second,d=p.first;
        if(d>dist2[v]) continue;
        for(int i=0;i<G[v].size();i++){
            edge &e=G[v][i];
            int d2=d+e.cost;
            if(d2<dist[e.to]){
                swap(dist[e.to],d2);
                que.push(P(dist[e.to],e.to));
            }
            if(dist2[e.to]>d2&&dist[e.to]<=d2){
                dist2[e.to]=d2;
                que.push(P(dist2[e.to],e.to));
            }
        }
    }
}

也可以用次短路与最短路比较,判断最短路是否唯一

最小生成树

一般有以下两个贪心算法,复杂度都是 o(ElogE)

Prim

该算法是从一个起点出发,一直添加最短边的点,直到将点加完

#include<queue>
#include<vector>
using namespace std;
typedef pair<int,int> P;
vector<P /*first->to second->cost*/ > G[V];

int prim(int start){
    priority_queue<P/*first->cost  second->id*/,vector<P>,greater<P> > que;
    que.push(make_pair(0,start));
    int res=0;

    while(!que.empty()){
        int value=que.top().first,t=que.top().second;que.pop();
        if(used[t]) continue;
        used[t]=true;
        res+=value;

        for(int i=0;i<G[t].size();i++){
            int to=G[t][i].first,cost=G[t][i].second;
            if(!used[to])
                que.push(make_pair(cost,to));
        }
    }
    return res;
}
//csdn编辑器打的代码 正确性未验证

Kruskal

一直添加权值最小的边,直到所有的点都已经合并到一个集合

#include<vector>
#define MAX_N 1000
using namespace std;
int par[MAX_N];
struct edge{int from,to,cost;};
edge es[MAX_N];
int E,V;

int find(int x){return x==par[x]?x:par[x]=find(par[x]);}
bool cmp(edge x,edge y){return x.cost<y.cost;}

int kruskal(int start){
    for(int i=0;i<=V;i++) par[i]=i;
    sort(es,es+E,cmp);
    int res=0;

    for(int i=0;i<E;i++){
        if(find(es[i].from)!=find(es[i].to)){
            par[es[i].from]=par[es[i].to];
            res+=es[i].cost;
        }
    }
    return res;
}
//csdn编辑器打的代码 正确性未验证
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值