什么方法好,和数据关系很大,和你使用的数据结构也有关。比如使用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(E⋅logE)
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编辑器打的代码 正确性未验证