深入浅出理解 图的 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)
算法分析
- 图的广度优先遍历一般用队列来实现,和深度优先遍历一样,依次对图中每一个未访问的顶点做BFS.
- 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;
}