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

图的遍历及其作用:


图的遍历主要分为两种:

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

展开阅读全文
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值