深度优先搜索(DFS),广度优先搜索(BFS)

原文:http://blog.163.com/wujiaxing009%40126/blog/static/71988399201241484630763/

图的遍历一般由两者方式:深度优先搜索(DFS),广度优先搜索(BFS),深度优先就是先访问完最深层次的数据元素,BFS其实就是层次遍历,每一层每一层的遍历。

1.深度优先搜索(DFS)  类似树的前序遍历

我一贯习惯有举例的方法来讲,示例如下:红色代表的是正搜索,蓝色代表回溯,最下面为标志数组。

图的遍历:DFS和BFS - 樂不思蜀 - 樂不思蜀
 

注意:DFS的搜索出来的序列不是每个人都是一样的,根据具体的程序可能出现不同的顺序。

程序设计:由对深度优先搜索的理解,我们可以知道我们从根节点的开始向下搜索,注意题目中给出的是连通的图,在实际情况下可能有非连通的图,图中根节点v1的子节点为v2,v3,我们可以访问它的子节点按照从左到右也可以从右到左,我们这里选择从左到右的方向,先访问v2,访问v2后又以v2作为子节点开始从左到右访问,但是我们可能注意到访问到v5时,如果我们仅仅从图的相关节点的连通性来判断是否应该继续深入的话,我们会在v5节点形成一个环,从而造成程序的死循环,这里就需要一个标志数组来标志哪些节点是已经访问了的,这样不仅仅能够保证不形成环也可以为节点回溯提供保证。说到这里我们基本已经解决了连通图的DFS,我们主要到如果是非连通图我们不能通过子图的连通性来访问到所有的节点,所以下面一段代码就是必须的了;

 

for(i=0; i<n; i++)

if(!color[i])

{

printf(" V%d ", i+1);

DFS_Visit(G,i,n);

printf("\n");

}

这段代码从第一个节点测试到最后一个节点测试是否已经访问过,如果是非连通图,中间一定会有在第一次访问完成之后还有节点没有访问,所有利用标志数组就可以轻易的达到这个目的。

图测试用例.txt

1 2

1 3

2 4

2 5

4 8

5 8

3 6

3 7

6 7

测试程序:

 
#include <stdio.h>
#include <string.h>


const int GNumber = 8;
int G[GNumber][GNumber];
int color[GNumber];

void DFS_Visit(int G[][GNumber], int i, int n)
{
	int j;
 	color[i] = 1;

 	for(j=0; j< n; j++)
 	{
  		if(G[i][j] && !color[j])
  		{
   			printf(" V%d ", j+1);
   			color[j] = 1; 
   			DFS_Visit(G, j, n);
  		}
 	}
}

void DFS(int G[][GNumber], int n)
{
 	int i;
 	memset(color, 0, sizeof(color));

 	for(i=0; i<n; i++)
 	{
  		if(!color[i])
  		{
   			printf(" V%d ", i+1);
   			DFS_Visit(G,i,n);
   			printf("\n");
  		}
 	}
}

int main()
{
 	FILE *fr;
 	int i,j;
 	fr = fopen("图的遍历测试用例.txt","r");
 	if(!fr)
 	{
  		printf("fopen failed\n");
  		return -1; 
 	}
 	while(fscanf(fr,"%d%d", &i, &j) != EOF)
 	{
  		G[i-1][j-1] = 1;
  		G[j-1][i-1] = 1;
 	}
 	DFS(G,GNumber);

 	getchar();
 	return 0;
}


程序结果:

 V1  V2  V4  V8  V5  V3  V6  V7

请按任意键继续. . .

2.广度优先搜索(BFS)

示例如下:红色代表的是正搜索,最下面为标志数组。


 

程序设计:一个图如果要层次遍历的话,那么他应该是连通图,不然层次没法分,对一个连通图进行层次遍历,我们模拟一下就知道,如上图,当访问了v1节点后,我们就应该访问第二层都为它的子节点,我们这里以顺序从左到右访问,那么应该访问的是v2v3,为了能够表示访问的顺序,我们这里设置一个先进先出的结构,很明显就是一个队列了,要访问前v1将它放入队列,然后访问v1,并将他的子节点放到队列中:v1v2v3;访问了v1后出队输出,我们继续访问队列中的元素,以队列中的元素为根节点找他的子节点并加入到队列中,知道队列为空。这里的标志数组标志着节点是否进入过队列,这里由于元素很少而且队列中的元素肯定不会超过顶点个数,所以我直接使用的数组来模拟队列。

测试程序:

 
#include <stdio.h>
#include <string.h>

const int GNumber = 8;
int G[GNumber][GNumber];
int color[GNumber];// 防止回环 

struct Queue//用数组模拟队列 
{
 	int queue[GNumber];
 	int start;
 	int end;
} MyQueue;

void BFS(int G[][GNumber], int n)
{
 	int j;
 	MyQueue.queue[MyQueue.end++] = 0;
 	color[0] = 1;

 	/* 
 	for(j=0; j<n; j++)
 	{
 		if(G[i][j] && !color[j])
 		{
 			printf(" V%d ", j+1);
 			color[j] = 1;
 			MyQueue.queue[MyQueue.end++] = j;
 		}
 	}
 	*/

 	while(MyQueue.end != MyQueue.start)
 	{
  		//color[MyQueue.start] = 1;
  		for(j=0; j<n; j++)
  		{
   			if(G[MyQueue.start][j] && !color[j])
   			{
    				color[j] = 1;
    				MyQueue.queue[MyQueue.end++] = j;
   			}
  		}
  		printf(" V%d ", MyQueue.queue[MyQueue.start++]+1);
 	}
}

int main(int argc, char **argv)
{
 	FILE *fr;
 	int i,j;
 	fr = fopen("图的遍历测试用例.txt","r");
 	if(!fr)
 	{
  		printf("fopen failed\n");
  		return -1; 
 	}
 	while(fscanf(fr,"%d%d", &i, &j) != EOF)
 	{
  		G[i-1][j-1] = 1;
  		G[j-1][i-1] = 1;
 	}
 	memset(&MyQueue, 0, sizeof(MyQueue));
 	memset(color, 0, sizeof(color));

 	BFS(G,GNumber);

 	getchar();
 	return 0;
}

程序结果:

 V1  V2  V3  V4  V5  V6  V7  V8 请按任意键继续. . .

自己码一遍

//dfs
#include<stdio.h>
#define N 8
int G[N][N]={0};
int visit[N]={0};
void dfs(int i)
{
	visit[i]=1;
	printf("V%d ",i+1);
	for(int j=0;j<N;j++)
	{
		if(!visit[j] && G[i][j]==1)	
		{
			dfs(j);
		}
	}	
} 
int main()
{
	int i,j;
	while(~scanf("%d %d",&i,&j))//无向图 
	{
		G[i-1][j-1]=1;
		G[j-1][i-1]=1;
	}
	for(int i=0;i<N;i++) //如果图为非连通图,单从一个顶点遍历是不可能遍历到全部节点的 
	{
		if(visit[i]==1){ 
			continue;
		}
		dfs(i);	
	}	
	return 0;
}
//bfs
#include<stdio.h>
#include<queue>
using namespace std; 
#define N 8
int G[N][N]={0};
int visit[N]={0};
queue<int>Q;
void bfs(int i)
{
	Q.push(i);
	visit[i]=1;
	while(!Q.empty())
	{
		int front =Q.front();
		printf("V%d ",front+1);
		Q.pop();
	 	for(int j=0;j<N;j++)
	 	{
	 		if(!visit[j] && G[front][j]==1)
	 		{
	 			visit[j]=1;
	 			Q.push(j);
			}
		}
	}
}
int main()
{
	int i,j;
	while(~scanf("%d %d",&i,&j))//无向图 
	{
		G[i-1][j-1]=1;
		G[j-1][i-1]=1;
	}
	for(i=0;i<N;i++)
	{
		if(visit[i]==1)continue;
		bfs(i);
		printf("\n");
	}
	return 0; 
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值