图的深度优先遍历是树的先序遍历的延伸,先序遍历是DFS的特殊情况。从图中任选一个顶点v,访问后开始随意选一个邻接点,从新的邻接点开始进行DFS,以此类推,这是一个递归的过程,直到遍历完图中所有结点。
实际上,递归算法的本质就是一个栈,递归算法消耗大量的内存存储临时变量和中间状态,但是代码更简单。(至于用不用递归,就看实际需求吧)
图的非递归深度优先遍历,引入一个数组和一个栈,数组visit[]用来记录每个结点被访问的状态,没有被访问是0,访问过是1,另外引入一个栈,用来存储访问过的结点,每访问一个结点就将它入栈,当某个结点不存在邻接点时,出栈,然后寻找栈顶元素的邻接点,如果没有,继续出栈,直到找到邻接点为止,如果栈空了仍然没有找到,说明一个连通图已经被遍历过了。如果是不连通图,从visit数组中可以发现还有没有遍历到的结点,就从这些结点中(另外一个图中)随机挑选一个结点作为第一个结点入栈,开始新的遍历。
图的广度优先遍历是一个非递归的过程,当然可以写成递归,但是没必要。广度优先遍历借助于一个visit数组和一个队列来实现,首先访问图中任意一个结点并入队列,然后将该结点的所有邻居入队,然后队列的第一个结点出队,访问队头结点,并将队头结点的邻居入队,然后队头结点出队,以此类推。。。最终就可以实现图的广度优先遍历。
接下来我们将以上图为例进行图的代码演示,使用邻接矩阵方法表示一个图,并实现图的深度优先遍历(递归)以及图的广度优先遍历。
Graph_matrix.h
#ifndef _GRAPH_MATRIX_H
#define _GRAPH_MATRIX_H
#define MAX_VERTEX_NUM 100
typedef struct
{
int n;//顶点数
int e;//边数
char vexs[MAX_VERTEX_NUM];
int edges[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
}Graph;
void CreateMGraph(Graph *G);
void DFS_Recur(Graph G, int i);
void BFS(Graph G);
#endif // !_GRAPH_MATRIX_H
SeqQueue.h
#ifndef _SEQQUEUE_H
#define _SEQQUEUE_H
#define MAXSIZE 50
typedef struct Queue * SeqQueue;
struct Queue
{
int front; //队伍头
int rear; //队伍尾
char data[MAXSIZE];//数据
};
SeqQueue Create();//初始化操作,建立一个空队列
int IsEmpty(SeqQueue Sq);//是否为空
int Del(SeqQueue Sq);//出队
void Insert(SeqQueue Sq, char val);//插入
int GetHead(SeqQueue Sq);//获取队列的头元素
void Clear(SeqQueue Sq);//将队列清空
void Destory(SeqQueue Sq);//销毁队列
#endif // !_SEQQUEUE_H
Operation.cpp
#include <stdio.h>
#include <stdlib.h>
#include"Graph_matrix.h"
#include"SeqQueue.h"
void CreateMGraph(Graph *G)
{
//方便起见,我们直接自动创建这个图。
int i, j;
G->n = 8;
G->e = 9;
for (int i = 0; i < G->n; i++)
{
G->vexs[i] = 'A' + i;
}
for(i = 0;i < G->n;i++)
for (j = 0; j < G->n; j++)
{
G->edges[i][j] = 0;//初始化为最大值,我们假设0表示两个结点无连接
}
G->edges[0][1] = 1;
G->edges[0][2] = 1;
G->edges[1][0] = 1;
G->edges[1][3] = 1;
G->edges[1][5] = 1;
G->edges[2][0] = 1;
G->edges[2][4] = 1;
G->edges[2][6] = 1;
G->edges[3][1] = 1;
G->edges[3][7] = 1;
G->edges[4][2] = 1;
G->edges[4][6] = 1;
G->edges[5][1] = 1;
G->edges[5][7] = 1;
G->edges[6][2] = 1;
G->edges[6][4] = 1;
G->edges[7][3] = 1;
G->edges[7][5] = 1;
}
int visit[MAX_VERTEX_NUM] = {0};//标志某个顶点是否被访问过
void DFS_Recur(Graph G, int i)//深度优先遍历算法的递归方法
{
int j;
visit[i] = 1;//将当前访问顶点的访问标志位设置为1
printf("%c", G.vexs[i]);
for (j = 0; j < G.n; j++)
{
//若j对应的顶点未被访问,则访问
if (G.edges[i][j] == 1 && visit[j] == 0)
DFS_Recur(G, j);
}
}
int visit1[MAX_VERTEX_NUM] = { 0 };//标志某个顶点是否被访问过
void BFS(Graph G)//广度优先遍历
{
int i, j;
SeqQueue Q = Create();//创建一个队列并初始化
for (i = 0; i < G.n; i++)
{
visit1[i] = 0;
}
i = 0;
if (visit1[i] == 0)
{
visit1[i] = 1;
printf("%c", G.vexs[i]);
Insert(Q, 65 + i); //将结点对应的字母入队ABCDEFG
}
while (!IsEmpty(Q))
{
//printf("%d", i);
for (j = 0; j < G.n; j++)
{
//访问当前结点的待访问临近结点
if (G.edges[i][j] == 1 && visit1[j] == 0)
{
visit1[j] = 1;
printf("%c", G.vexs[j]);
Insert(Q, 65 + j);//入队
}
}
Del(Q);
i = GetHead(Q) - 65;
}
}
SeqQueue.cpp
#include "SeqQueue.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
SeqQueue Create()
{
SeqQueue Sq = (SeqQueue)malloc(sizeof(struct Queue));//分配空间
Sq->front = Sq->rear = -1;
memset(Sq->data, 0, MAXSIZE * sizeof(char));//清空内存空间,从Sq的第一个空间data开始清空一整片内存
return Sq;
}
int IsEmpty(SeqQueue Sq)
{
if (Sq->front == Sq->rear)
{
return 1;
}
return 0;
}
void Insert(SeqQueue Sq, char val)
{
if (Sq->rear == MAXSIZE - 1)//如果队列已满
{
printf("队列已满,无法再插入元素!\n");
return;
}
//如果队列是空队列
if (IsEmpty(Sq))
{
Sq->front = Sq->rear = 0;
Sq->data[Sq->rear] = val;
Sq->rear++;
}
else
{
Sq->data[Sq->rear] = val;
Sq->rear++;
}
}
int Del(SeqQueue Sq)
{
//空队列
if (IsEmpty(Sq))
{
printf("队列为空,无元素可以弹出!\n");
return 10000;
}
int temp = Sq->data[Sq->front];
Sq->front++;
return temp;
}
int GetHead(SeqQueue Sq)
{
//空队列
if (IsEmpty(Sq))
{
//printf("队列为空,无元素可取!\n");
return 10000;
}
//获取元素
return Sq->data[Sq->front];
}
void Clear(SeqQueue Sq)
{
Sq->front = Sq->rear = -1;
printf("队列已清空!\n");
}
void Destory(SeqQueue Sq)
{
free(Sq);
printf("队列已销毁!\n");
}
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include"Graph_matrix.h"
int main()
{
Graph G;
CreateMGraph(&G);
DFS_Recur(G, 0);
printf("\n");
BFS(G);
system("pause");
}