图的遍历及其作用:
图的遍历主要分为两种:
1.深度优先遍历(dfs)
2.广度优先遍历(bfs)
1.DFS遍历:通过深度优先搜索来理解
遍历代码: Void dfs(int k); { printf(“%d”,k); f[k]=true;//将k标记为已经被遍历过的点 for (int j=1;j<=n;j++) if ((!f[j])&& a[k][j]) dfs(j);//查找和k连通的点 }
主程序: memset(f,0,sizeof(f)); for (int i=1;i<=n;i++) if (!f[i]) dfs(i);
以上为邻接矩阵的DFS 复杂度:O(n^2)
邻接表:复杂度O(E)
邻接表的DFS for (int i=link[k];i;i=e[i].next)//由link数组开始和k相连的每一条边,这句话要牢记 if (!vis[e[i].y]) vis[e[i].y]=1,dfs(e[i].y);
2.BFS遍历:结合广度优先搜索,运用队列优化:复杂度相对深度较好
Void bfs(int i); { memset(q,0,sizeof(q)); int head=0,tail=1; q[1]=i; f[i]=true; while (head<tail) { k=q[++head]; cout>>k; for (int j=1;j<=n,j++) if (a[k][j] && !f[j])//邻接表就是在这两句的基础上修改,和DFS基本类似 { q[++tail]=j; f[j]=true; } } }
3.例题一:田野上的环HLUOJ#301
题目大意:求与1连通的所有点
基本思路:显然,通过DFS遍历找到与1相连的点,并用vis数组记录最后升序输出即可。
基本代码如下:
#include<bits/stdc++.h> using namespace std; struct cow { int y,next; }e[600000]; int lin[5000],t=0,n,m; bool vis[600000]; void inse(int xxx,int yyy) { e[++t].next=lin[xxx]; lin[xxx]=t; e[t].y=yyy; }//构建邻接表 void dfs(int k) { vis[k]=1; for (int i=lin[k];i;i=e[i].next) if (!vis[e[i].y]) dfs(e[i].y); }//DFS遍历 void out(){ bool flag=1; for (int i=1;i<=n;i++) if (!vis[i]){ cout<<i<<endl; flag=0; } if (flag) cout<<0<<endl; }//输出结果 int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int xx,yy; cin>>xx>>yy; inse(xx,yy); inse(yy,xx);//无向图,双向建图 } dfs(1); out(); }
4.犯罪团伙(HLUOJ#302~304)
题目大意:求连通分量的个数
题目思路:每一次对没有遍历到的点进行DFS并标记,然后累加,最后遍历的次数即为所要求的连通分量的个数
1.邻接矩阵+DFS
#include <bits/stdc++.h> using namespace std; int n,m,xx,yy,s=0,a[1010][1010]={}; bool vis[1010]={}; void dfs(int k) { vis[k]=true; for (int i=1;i<=n;i++) if (!vis[i]&&a[k][i]==1) dfs(i); } int main() { cin>>n>>m; for (int i=1;i<=m;i++) { cin>>xx>>yy; a[xx][yy]=1;a[yy][xx]=1; } for (int i=1;i<=n;i++) if (!vis[i]) { s++; dfs(i); } cout<<s; return 0; }
2.邻接表+DFS
#include<bits/stdc++.h> using namespace std; struct cow { int y,next; }e[600000]; int lin[5000],t=0,n,m,sum=0; bool vis[600000]={}; void inse(int xxx,int yyy) { e[++t].next=lin[xxx]; lin[xxx]=t; e[t].y=yyy; } void dfs(int k) { vis[k]=1; for (int i=lin[k];i;i=e[i].next) if (!vis[e[i].y]) dfs(e[i].y); } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int xx,yy; cin>>xx>>yy; inse(xx,yy); inse(yy,xx); } for (int i=1;i<=n;i++) if (vis[i]==0) { dfs(i); sum++; } cout<<sum; return 0; }
3.邻接表+BFS
answer
#include<bits/stdc++.h> using namespace std; int n,m; struct edge { int next,y; }e[400010*2]; int linkk[50010]; int tott=0; bool vis[50010]={}; int q[50010]; int ans=0; void read(int &x) { int f=1;x=0;char s=getchar(); for(;s<'0'||s>'9';s=getchar()) if(s=='-') f=-1; for(;s>='0'&&s<='9';s=getchar()) x=(x<<3)+(x<<1)+s-48; x*=f; }//快读 inline void insert(int xx,int yy) { e[++tott].y=yy; e[tott].next=linkk[xx];linkk[xx]=tott; } void bfs(int x) { int head=0,tail=0; q[++tail]=x; vis[x]=1; while(head++<tail) for(int i=linkk[q[head]];i;i=e[i].next) if(!vis[e[i].y]) vis[e[i].y]=1,q[++tail]=e[i].y; return; } int main() { read(n);read(m); int x,y; for(int i=1;i<=m;++i) { read(x);read(y); insert(x,y);insert(y,x); } for(int i=1;i<=n;++i) if(!vis[i]) ans++,bfs(i); printf("%d",ans); return 0; }
5.几何图形还原
题目大意:求连通所有点的,字典序最小的环
题目思路:显然,可以利用DFS+回溯枚举每一个点,且从小到大枚举,用path数组记录当前枚举的点数,第一个符合方案的即为字典许最小的.
针对卡读入的友情提醒:
用while(cin>>~~~)解决
用1.oj内的评测系统调试
2.用文件输入输出调试
千万不要再c++内调试,因为系统无法判断你是否已经输入完毕
代码如下:
#include<bits/stdc++.h> using namespace std; int vis[1000]={},a[1000][1000]={},path[1000]={},n,r; void dfs(int d,int s)//点,边 { if (r==1) return; if (a[d][1]==1&&s==n) { for (int i=1;i<=n-1;i++) cout<<path[i]<<' '; cout<<path[n]; r=1; }//输出方案 for (int i=1;i<=n;i++) if (vis[i]==0&&a[d][i]==1) { vis[i]=1; path[s+1]=i; dfs(i,s+1); vis[i]=0; }//DFS+回溯 } int main() { int x,y; cin>>n; while (cin>>x>>y) a[x][y]=a[y][x]=1; vis[1]=1;path[1]=1; dfs(1,1); }