图的遍历及其作用:
图的遍历主要分为两种:
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>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
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
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
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
#include
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
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);
}