图的深度优先遍历和广度优先遍历

深入浅出理解 图的 DFS 和 BFS


一、邻接表法建立图

创建图的方法有邻接矩阵和邻接表法。

  • 邻接矩阵把边的关系包含在一个矩阵中,虽然很方便,但是,当图中的定点数远大于边数时,浪费了很大的空间。
  • 邻接表把边与顶点的关系存在一个叫弧(边)的数据结构中,有几条边就创建几条弧。节省了内存空间,但也怎加了对各种结构体之间关系的理解难度。
  • 其实图的DFS和BFS就算法上来看,是非常简单的,但是要很流利的写出图的这两种遍历的算法,要对结构体之间关系有较好的理解。

下面先来看数据结构:

typedef enum{DG,DN,UDG,UDN}GraphyType; //图的种类
typedef struct ArcNode  //弧结点
{
	int adjVerxIndex;    //该弧指向的顶点在图中顶点数组的下标
	struct ArcNode* nextArc;  //该弧在邻接表中的下一条弧
}ArcNode;

typedef struct VertexNode  //顶点
{
	char data;   //顶点数据
	ArcNode* firstArc;   //该顶点相连的第一条弧
}VertexNode;

typedef struct graphy //邻接表(图)
{
	VertexNode vertex[MAXSIZE];
	int verNum,arcNum;
	GraphyType type;//图的种类,若要简单可以舍去这一项
}*AdjList;

再来看如何创建图:

int Locate(AdjList adj , char des) //通过顶点数据找到顶点下标
{
	for(int i = 0; i< adj->verNum; ++i)
	{
		if(des == adj->vertex[i].data)
		{
			return i;
		}
	}
	return -1;
}
void CreatAdjList(AdjList* adj)//创建图
{
	*adj = (AdjList)malloc(sizeof(graphy));
	cout<<"请输入顶点数:"<<endl;
	cin>>(*adj)->verNum;
	cout<<"请输入边数:"<<endl;  
	cin>>(*adj)->arcNum;
	(*adj)->type = DN;
	
	int i = 0;
	cout<<"请依次输入顶点的值:"<<endl;
	for(i = 0; i < (*adj)->verNum; ++i)
	{	
		cin>>(*adj)->vertex[i].data;
		(*adj)->vertex[i].firstArc = NULL;
	}
	
	for(i = 0; i < (*adj)->arcNum; ++i)
	{
		char start,end;
		int ixS,ixE;
		cout<<"请输入第"<<i<<"条边的两个顶点值:"<<endl;
		cin>>start>>end;
		ixS = Locate(*adj,start);
		ixE = Locate(*adj,end);
		ArcNode* node = (ArcNode*)malloc(sizeof(ArcNode));
		node->nextArc = (*adj)->vertex[ixS].firstArc;
		node->adjVerxIndex = ixE;
		(*adj)->vertex[ixS].firstArc = node;//头插法
	}
}

没什么难以理解的,就是把图结构体中的变量初始化,唯一要注意的是,在创建边的时候,采用头插还是尾插的方法对最后遍历结果有一定影响,实际上先遍历哪一个邻接点都没有关系,所以图的两种遍历结果其实都是不唯一的,只要某些关键点的前后顺序一致就行了。


二、图的深度优先遍历(DFS)

算法分析

图的深度优先遍历其实就是通常说的回溯,一般用递归实现,做法是依次对每个未访问的结点做DFS。

DFS:访问该顶点,然后在一个循环中分别对每一个邻接点做DFS,直到所有邻接点都已做完DFS返回,可见DFS使用了递归。

代码:

void Dfs(AdjList adj, int i)
{
	if(flag[i])
	{
		return;
	}
	cout<<adj->vertex[i].data<<"  ";
	flag[i] = true;
	ArcNode* p = adj->vertex[i].firstArc; 
	while(p)
	{
			Dfs(adj,p->adjVerxIndex);
			p = p->nextArc;
	}
}

三、图的广度优先遍历(BFS)

算法分析

  1. 图的广度优先遍历一般用队列来实现,和深度优先遍历一样,依次对图中每一个未访问的顶点做BFS.
  2. BFS:访问一个节点之前,现将这个顶点入队,然后在访问,访问完毕之后,将他的所有邻接点入队。然后判断队列是否为空,若为空则表示访问完毕,退出循环,可见BFS未用递归。

代码:

void Bfs(AdjList adj,int i)
{
	q.push(i);	
	while(!q.empty())
	{				
		if(!flag[q.front()])
		{
			cout<<adj->vertex[q.front()].data<<"  ";
		    flag[q.front()] = true;	    
		}
		ArcNode* p = adj->vertex[q.front()].firstArc;
		q.pop();
		while(p)
	    {
		q.push(p->adjVerxIndex);
		p = p->nextArc;
	    }
	} 
	
} 

四、整体实现及测试结果

1. 画图分析:

图

2. 整体代码:

#include<iostream>
#include<malloc.h>
#include<queue>
#include<string.h>
#define MAXSIZE 20
using namespace std;
typedef enum{DG,DN,UDG,UDN}GraphyType;
bool flag[MAXSIZE] = {false}; 
queue<int> q;
typedef struct ArcNode
{
	int adjVerxIndex;
	struct ArcNode* nextArc;
}ArcNode;

typedef struct VertexNode
{
	char data;
	ArcNode* firstArc;
}VertexNode;

typedef struct graphy
{
	VertexNode vertex[MAXSIZE];
	int verNum,arcNum;
	GraphyType type;
}*AdjList;

void Dfs(AdjList adj, int i);
void Bfs(AdjList adj,int i);
int Locate(AdjList adj , char des)
{
	for(int i = 0; i< adj->verNum; ++i)
	{
		if(des == adj->vertex[i].data)
		{
			return i;
		}
	}
	return -1;
}
void CreatAdjList(AdjList* adj)
{
	*adj = (AdjList)malloc(sizeof(graphy));
	cout<<"请输入顶点数:"<<endl;
	cin>>(*adj)->verNum;
	cout<<"请输入边数:"<<endl;  
	cin>>(*adj)->arcNum;
	(*adj)->type = DN;
	
	int i = 0;
	cout<<"请依次输入顶点的值:"<<endl;
	for(i = 0; i < (*adj)->verNum; ++i)
	{	
		cin>>(*adj)->vertex[i].data;
		(*adj)->vertex[i].firstArc = NULL;
	}
	
	for(i = 0; i < (*adj)->arcNum; ++i)
	{
		char start,end;
		int ixS,ixE;
		cout<<"请输入第"<<i<<"条边的两个顶点值:"<<endl;
		cin>>start>>end;
		ixS = Locate(*adj,start);
		ixE = Locate(*adj,end);
		ArcNode* node = (ArcNode*)malloc(sizeof(ArcNode));
		node->nextArc = (*adj)->vertex[ixS].firstArc;
		node->adjVerxIndex = ixE;
		(*adj)->vertex[ixS].firstArc = node;
	}
}
void Print(AdjList adj)
{
	for(int i = 0; i < adj->verNum; ++i)
	{
		if(flag[i])
		{
			continue;
		}
		cout<<"DFS:"<<endl; 
	    Dfs(adj,i);
	 
	}
	memset(flag,0,MAXSIZE);
	for(int i = 0; i < adj->verNum; ++i)
	{
		if(flag[i])
		{
			continue;
		}
		cout<<endl<<"BFS:"<<endl; 
	    Bfs(adj,i);
	}
}
void Dfs(AdjList adj, int i)
{
	if(flag[i])
	{
		return;
	}
	cout<<adj->vertex[i].data<<"  ";
	flag[i] = true;
	ArcNode* p = adj->vertex[i].firstArc; 
	while(p)
	{
			Dfs(adj,p->adjVerxIndex);
			p = p->nextArc;
	}
}
void Bfs(AdjList adj,int i)
{
	q.push(i);	
	while(!q.empty())
	{				
		if(!flag[q.front()])
		{
			cout<<adj->vertex[q.front()].data<<"  ";
		    flag[q.front()] = true;	    
		}
		ArcNode* p = adj->vertex[q.front()].firstArc;
		q.pop();
		while(p)
	    {
		q.push(p->adjVerxIndex);
		p = p->nextArc;
	    }
	} 
	
} 
int main()
{
	AdjList adj;
    CreatAdjList(&adj);
    Print(adj);
	return 0;
} 

3.结果:

结果

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值