【算法笔记】图的遍历+例题全解

图的遍历及其作用:

《【算法笔记】图的遍历+例题全解》

图的遍历主要分为两种:

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); }

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/Ronaldo7_ZYB/article/details/80147758
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞