一、前言
图的深度优先遍历和广度优先遍历与二叉树的遍历类似,都是采用递归算法,对每一个点进行访问。
二、深度优先遍历(DFS)
1.算法实现
采用邻接矩阵表示图的深度优先搜索遍历
typedef int Boolean; //Boolean是布尔类型,其值是TRUE或FALSE
Boolean visited[MAX]; //访问标志的数组
//邻接矩阵的深度优先递归算法
void DFS(AMGraph G,int v){ //图G为邻接矩阵类型
cout<<v;visited[v] = true; //访问第V个顶点
for(w = 0; w < G.vexnum; w++)}//依次检查邻接矩阵v所在的行
if((G.arcs[v][w]!=0)&&(!visited[w]))
DFS(G,w);
}
//w是v的邻接点,如果w未访问,则递归调用DFS
}
//邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G)
{
int i;
for(i = 0; i<G.vexnum;i++)
visited[i] = FALSE; //初始所有顶点状态都是未访问
for(i= 0;i<G.vexnum;i++)
if(!visited[i]) //对未访问过的顶点调用DFS,若是连通图,只会执行一次
DFS(G,i);
}
采用邻接表表示图的深度优先搜索遍历
//邻接表的深度优先递归算法
void DFS(ALGraph G,int v){
ArcNode *p;
cout<<v;visited[v] = true; //访问第v个顶点
p = G.adjlist[v].firstarc; //p指向v的第一个邻接点
while(p!=NULL){
w = p->adjvex; //邻接点序号为w
if(!visited[w])
DFS(G,w); //若w为未访问过的顶点,则递归调用DFS
p = p->nextarc; //p指向v的下一个邻接点
}
}
//邻接表的深度遍历操作
void DFSTraverse(ALGraph G)
{
int i;
for(i = 0;i<G.vexnum;i++)
visited[i] = FALSE; //初始所有顶点状态都是未访问
for(i= 0;i<G.vexnum;i++)
if(!visited[i]) //对未访问过的顶点调用DFS,若是连通图,只会执行一次
DFS(G,i);
}
2.算法效率分析
- 用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描该顶点所在行,时间复杂度为O(n^2)。
- 用邻接表来表示图,虽然有2e个边结点,但只需要扫描e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为O(n+e)。
结论
- 稠密图适于在邻接矩阵上进行深度遍历
- 稀疏图适于在邻接表上进行深度遍历
三、广度优先遍历(BFS)
1.算法实现
采用邻接矩阵表示图的广度优先搜索遍历
//邻接矩阵的广度遍历算法
void BFS(Graph G,int v)
{
int i,j;
Queue Q;
// 初始化访问标记数组
for(i=0;i<G.vexnum;i++)
visited[i]=FALSE;
// 初始化队列
InitQueue(Q);
// 遍历所有顶点
for(i=0;i<G.vexnum;i++)
{
// 如果顶点未被访问
if(!visited[i])
{
// 标记顶点为已访问
visited[i]=TRUE;
// 打印顶点
printf("%c",G.vexs[i]);
// 将顶点入队
EnQueue(Q,i);
// 当队列不为空时
while(!QueueEmpty(Q))
{
// 出队一个顶点
DeQueue(Q,i);
// 遍历该顶点的所有邻接顶点
for(j=0;j<G.vexnum;j++)
{
// 如果邻接顶点未被访问且存在边
if(G.arc[i][j]==1&&!visited[j])
{
// 标记邻接顶点为已访问
visited[j]=TRUE;
// 打印邻接顶点
printf("%c",G.vexs[j]);
// 将邻接顶点入队
EnQueue(Q,j);
}
}
}
}
}
}
采用邻接表表示图的深度优先搜索遍历
//采用邻接表表示图的广度遍历算法
void BFS(Graph G)
{
int i;
ArcNode *p;
Queue Q;
// 初始化访问标记数组
for(i=0;i<G.vexnum;i++)
visited[i]=FALSE;
// 初始化队列
InitQueue(Q);
// 遍历所有顶点
for(i=0;i<G.vexnum;i++)
{
// 如果顶点未被访问
if(!visited[i])
{
// 标记顶点为已访问
visited[i]=TRUE;
// 打印顶点
printf("%c",G.vexs[i]);
// 将顶点入队
EnQueue(Q,i);
// 当队列不为空时
while(!QueueEmpty(Q))
{
// 出队一个顶点
DeQueue(Q,i);
// 遍历该顶点的所有邻接顶点
p=G.adjlist[i].firstarc;
while(p!=NULL)
{
// 如果邻接顶点未被访问且存在边
if(!visited[p->adjvex])
{
// 标记邻接顶点为已访问
visited[p->adjvex]=TRUE;
// 打印邻接顶点
printf("%c",G.vexs[p->adjvex]);
// 将邻接顶点入队
EnQueue(Q,p->adjvex);
}
p=p->nextarc;
}
}
}
}
}
2.算法效率分析
- 用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描该顶点所在行,时间复杂度为O(n^2)。
- 用邻接表来表示图,虽然有2e个边结点,但只需要扫描e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为O(n+e)。
四、DFS和BFS算法效率比较
- 空间复杂度相同,都是O(n)(借用了堆栈或队列);
- 时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。