(1)邻接矩阵:
#include#include #include #define MAXN 0x7fffffff//没有路径(边的长度为极限); #define maxn 9999//数据边的最大值; #define MAX 0x7fffffff/3//两极限边相加可能超过int 大小; using namespace std; int m,n;//点的个数和边的个数; int s[maxn][maxn]; void adjacency_matrix_storage() { cin>>m>>n; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) s[i][j]=MAXN; for(int i=1;i<=n;++i) { int s1,s2,weight; cin>>s1>>s2>>weight; s[s1][s2]=weight; //s[s2][s1]=weight; 如果是无向边就加上这句; } } int main() { adjacency_matrix_storage(); //后面是其他操作; return 0; }
(2)邻接表:
#include#include #include #define maxn 99999 #define MAXN 0x7fffffff/3 using namespace std; struct node { int a,b,w,next; }; node edge[maxn];//用邻接表存储边; int head[maxn],sum=1;//存储顶点,sum为下标; int m,n; //顶点个数和边的个数; void push(int a,int b,int c) { edge[sum].a=a; edge[sum].b=b; edge[sum].w=c; edge[sum].next=head[a]; head[a]=sum++; } void Initialization() { sum=1; for(int i=1;i<=m*n;++i) { head[i]=-1;//用-1作为标志表明链表的结束; edge[i].w=MAXN; } } int main() { int i,j; cin>>m>>n; Initialization(); for(i=1;i<=n;++i) { int a,b,c; cin>>a>>b>>c; push(a,b,c);//有向边; } cin>>j; cout<<endl; for(int k=head[j];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边; { int l=edge[k].b;//L和顶点J的关系是:L和J之间存在一条从J指向L的有向线段; cout< ' '< endl; } //······其他操作; return 0; }
一般情况下使用邻接表存储会降低使用空间大小和有效节约时间;但如果是比较稠密的图这两种方式就区别不大了;
2.图的遍历:
(1)DFS:
#include#include #include #define maxn 99999 #define MAXN 0x7fffffff/3 using namespace std; struct node { int a,b,w,next; }; node edge[maxn];//用邻接表存储边; int head[maxn],sum=1;//存储顶点,sum为下标; int m,n; //顶点个数和边的个数; bool visit[maxn]={false}; void push(int a,int b,int c) { edge[sum].a=a; edge[sum].b=b; edge[sum].w=c; edge[sum].next=head[a]; head[a]=sum++; } void Initialization() { sum=1; for(int i=1;i<=m*n*2;++i) { head[i]=-1;//用-1作为标志表明链表的结束; edge[i].w=MAXN; visit[i]=false; } } void DFS(int sum) { visit[sum]=true; if(edge[sum].b!=0) cout<<"-->"<<edge[sum].b; for(int k=head[sum];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边; if(!visit[edge[k].b]) DFS(edge[k].b); } int main() { int i,j; cin>>m>>n; Initialization(); for(i=1;i<=n;++i) { int a,b,c; cin>>a>>b>>c; push(a,b,c);//有向边; } cin>>j; cout<<endl; cout<<j; DFS(j); //······其他操作; return 0; }
(2)BFS:
#include#include #include #include #define maxn 99999 #define MAXN 0x7fffffff/3 using namespace std; struct node { int a,b,w,next; }; node edge[maxn];//用邻接表存储边; int head[maxn],sum=1;//存储顶点,sum为下标; int m,n; //顶点个数和边的个数; bool visit[maxn]={false},vis[maxn]={false}; void push(int a,int b,int c) { edge[sum].a=a; edge[sum].b=b; edge[sum].w=c; edge[sum].next=head[a]; head[a]=sum++; } void Initialization() { sum=1; for(int i=1;i<=m*n*2;++i) { head[i]=-1;//用-1作为标志表明链表的结束; edge[i].w=MAXN; visit[i]=vis[i]=false; } } void DFS(int sum) { visit[sum]=true; if(edge[sum].b!=0) cout<<"-->"<<edge[sum].b; for(int k=head[sum];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边; if(!visit[edge[k].b]) DFS(edge[k].b); } void BFS(int sum) { queue<int>que; que.push(sum); vis[sum]=true; cout<<sum; while(!que.empty()) { int su=que.front(); que.pop(); for(int k=head[su];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边; if(!vis[edge[k].b]) { que.push(edge[k].b); vis[edge[k].b]=true; cout<<"-->"<<edge[k].b; } } } int main() { int i,j; cin>>m>>n; Initialization(); for(i=1;i<=n;++i) { int a,b,c; cin>>a>>b>>c; push(a,b,c);//有向边; } cin>>j; cout<<endl; cout<<j; DFS(j); cout< "******************"<<endl; BFS(j); //······其他操作; return 0; }
一般来说这两者都有不小的缺点:DFS深度优先搜索会调用大量的栈空间,很容易爆栈;
而BFS内存耗费量大(需要开大量的数组单元用来存储状态)
总而言之,这两者都在时空上有很大的局限性;慎用!
3.求最短路径
(1)Flyoed:
#include#include #define MAXN 99999999 using namespace std; int dis[10][10],k,i,j,n,m; int main() { cin >> n >> m; for(i=1; i<=n; i++) for(j=1; j<=n; j++) if(i==j) dis[i][j]=0; else dis[i][j]=MAXN; for(i=1; i<=m; i++) { int a,b,c; cin >> a >> b >> c ; dis[a][b]=c; } for(k=1; k<=n; k++) for(i=1; i<=n; i++) for(j=1; j<=n; j++) if(dis[i][j]>dis[i][k]+dis[k][j] ) dis[i][j]=dis[i][k]+dis[k][j]; int start,end; cin >> start >> end ; cout << dis[start][end] <<endl; return 0; }
(2)Dijkstra:
#include#include #include #include const int MAXN = 1010; const int INF = 999999999; int n, k; int map[MAXN][MAXN]; bool visit[MAXN]; int pre[MAXN]; int dist[MAXN]; void init() { memset(visit, false, sizeof(visit)); for (int i = 1; i <= n; ++i){ for (int j = 1; j <= n; ++j) map[i][j] = INF; pre[i] = i; dist[i] = INF; } } void Dijkstra(const int v) { int i, j; int minValue, minNode; dist[1] = 0; visit[1] = true; for (i = 2; i <= n; ++i){ if (map[1][i] != INF){ dist[i] = map[1][i]; pre[i] = 1; } } for (i = 1; i <= n; ++i) { minValue = INF; minNode = 0; for (j = 1; j <= n; ++j){ if (!visit[j] && minValue > dist[j]){ minNode = j; minValue = dist[j]; } } if (minNode == 0) break; visit[minNode] = true; for (j = 1; j <= n; ++j) { if (!visit[j] && map[minNode][j] != INF && dist[j] > dist[minNode] + map[minNode][j]) { dist[j] = dist[minNode] + map[minNode][j]; pre[j] = minNode; } } if (minNode == v) break; } }
(3)SPFA:
long long SPFA(int st) { for(int i=1;i<=n;i++) sp[i]=inf; sp[1]=0; queue<int> q; q.push(st); while(!q.empty()) { int kai=q.front();q.pop(); for(int i=H[kai];i!=-1;i=E[i].next) { if(sp[E[i].v]>E[i].count+sp[kai]){ sp[E[i].v]=E[i].count+sp[kai]; q.push(E[i].v); } } } long long ans=0; for(int i=1;i<=n;i++) ans+=sp[i]; return ans; } void spfa(int s,int dis[]) { int i,pre[N]; bool used[N]; queue<int> q; memset(used,0,sizeof(used)); memset(pre,-1,sizeof(pre)); for(i=0; i) dis[i]=inf; dis[s]=0; used[s]=true; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); used[u]=false; for(i=0; i
三者的时间复杂度依次为:O(n^3),O(n^2),O(KE),其中E是边数,K是常数,平均值为2,图越稠密,K越大;
其中F·····和D·······算法比较的稳定,可是很慢啊,SPFA一般情况下比较快,可是一但图很稠密,就退化到N^2的复杂度;
但还是SPFA用的最多,也最好用,建议做最短路径的题用SPFA;
4.强联通分量
(1)Kosaraju
#include#include #define maxn 1005 using namespace std; bool s[maxn][maxn],s1[maxn][maxn]; bool visit[maxn]; int post[maxn]; int m,n,num=0,po=0; void DFS(int a) { visit[a]=true; for(int i=1;i<=n;++i) if(!visit[i]&&s[a][i]) DFS(i); post[++po]=a; } void Dfs(int a) { visit[a]=false; for(int i=1;i<=n;++i) if(visit[i]&&s1[a][i]) Dfs(i); } int main() { cin>>m>>n; for(int i=1;i<=n;++i) { int x,y; cin>>x>>y; s[x][y]=s1[x][y]=true; } for(int i=1;i<=n;++i) if(!visit[i]) DFS(i); for(int i=n;i>=1;--i) if(visit[post[i]]) { Dfs(post[i]); num++; } cout<<num; return 0; }
(2)tarjan算法
#include#include #include #define maxn 1005 using namespace std; struct node { int a,b,next; }; node edge[maxn*maxn]; int DFN[maxn],low[maxn]; int s[maxn],head[maxn]; bool visit[maxn]; int sum=1,num=0,tail=0; void push(int a,int b) { edge[sum].a=a; edge[sum].b=b; edge[sum].next=head[a]; head[a]=sum++; } void tarjan(int x) { DFN[x]=low[x]=++num; s[++tail]=x; visit[x]=true; for(int i=head[x];i!=-1;i=edge[i].next) { if(!DFN[edge[i].b]) { tarjan(edge[i].b); low[x]=min(low[x],low[edge[i].b]); } else if(visit[edge[i].b]) low[x]=min(low[x],DFN[edge[i].b]); } if(low[x]==DFN[x]) { do { printf("%d ",s[tail]); visit[s[tail--]]=false; }while (x!=s[tail+1]); printf("\n"); } return; } int main() { memset(head,-1,sizeof(head)); int m,n; cin>>n>>m; for(int i=1;i<=m;++i) { int x,y; scanf("%d %d",&x,&y); push(x,y); } for(int i=1;i<=n;++i) if(!DFN[i]) tarjan(i); return 0; }
Kosaraju算法有很大的优化的空间,例如可以改变存储方式、对搜索进行剪枝;
而tarjan算法比较好用,建议做强联通分量的题用tarjan算法;
5.最小生成树
(1)Prim算法
#include#include #include #include #define maxn 1005 using namespace std; struct node { int a,b,w,next; } node s[maxn*maxn]; int head[maxn]; bool visit[maxn]; int sum=1,n,m,sss; void push(int a,int b,int c) { s[sum].a=a; s[sum].b=b; s[sum].w=c; s[sum].next=head[a]; head[a]=sum++; } int sss=0; void Prim(int i) { int ss=1; sizeof(visit,false,sizeof(visit)); visit[i]=true; while(ss!=m) { int ssss=99999,sdf=0; for(int k=head[i];k!=-1;k=s[k].next) if(!visit[s[k].b]&&s[k].w<sss) { ssss=s[k].w; sdf=s[k].b; } sss+=s[sdf].w; ++ss; } } int main() { cin>>m>>n; for(int i=1;i<=n;++i) { int x,y,z; cin>>x>>y>>z; push(x,y,z); } Prim(1); cout<<sss; }
(2)Kruskal算法
#include#include #include #include #include #include #define maxn 510 #define MAXN 0x3f3f3f3f using namespace std; struct node { int a,b,cost,next; bool operator < (const node& b) const { if(!cost)return false; else return cost<b.cost; } }; node s[maxn*maxn]; int fa[maxn],m,n,q,sum=1; int push(int a,int b,int c) { s[sum].a=a; s[sum].b=b; s[sum].cost=c; sum++; } int find(int son) { if(fa[son]!=son) fa[son]=find(fa[son]); return fa[son]; } void unio(int a,int b) { int f1=find(a); int f2=find(b); if(f1!=f2) fa[f2]=f1; } int sssss=0,sss=0; void Kruskal() { sort(s+1,s+sum+1); for(int i=1;i<=sum;++i) { if(find(s[i].a)!=find(s[i].b)) { sssss+=s[i].cost; unio(s[i].a,s[i].b); sss++; } if(sss==n) return; } } int main() { freopen("water.in","r",stdin); freopen("water.out","w",stdout); for(int i=0;i<=500;++i) fa[i]=i; cin>>n; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) { int cost; cin>>cost; if(cost!=0) push(i,j,cost); } Kruskal(); cout<<sssss; }
Kruskal比较快,而Prim比较慢
6.拓扑排序