图的遍历
从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次,这一过程便叫做图的遍历。
深度优先遍历
也有称深度优先搜索(Depth First Search),简称DFS。有点像是树的先序遍历。
无向图的邻接矩阵存储方式深度优先遍历算法的代码实现:
// 访问标志数组
bool visited[MAXVN];
// 邻接矩阵深度优先递归算法
void DFS(MGraph G, int i) {
// 访问这个顶点,此处只是简单打印
printf("%c ", G.vexs[i]);
// 访问过后将标记设置为true
visited[i] = true;
// 开始操作结点i的邻接点
for(int j = 0; j < G.vertexNum; j++) {
// 当顶点j未访问过,同时顶点i和顶点j是相邻接的,才进行递归调用。
if(G.arc[i][j] == 1 && !visited[j]) {
DFS(G, j);
}
}
}
// 邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G) {
int i;
// 初始化所有顶点的初始化状态为未访问
for(i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
for(i = 0; i < G.vertexNum; i++) {
// 对所有未访问过的顶点调用DFS,若是连通图,则只会调用一次
if(!visited[i]) {
DFS(G,i);
}
}
}
邻接表结构存储的形式,
// 邻接表的深度优先递归算法
void DFS(MLGraph MLG, int i) {
EdgeNode *p = MLG.adjList[i].firstedge;
// 访问这个顶点,此处只是简单打印
printf("%c ", MLG.adjList[i].data);
// 访问过后将标记设置为true
visited[i] = true;
// 顶点i存在边(有邻接点),进行操作
while(p) {
// 当顶点i的邻接点未被访问过,才进行递归调用。
if(!visited[p->adjvex]) {
DFS(MLG, p->adjvex);
}
// 指向结点i的邻接点
p = p->next;
}
}
对于有向图而言,它只是对通道存在可行或不可行,算法上没有变化。
广度优先遍历
又称广度优先搜索算法(Breadth-First-Search),BFS,有点像是图的层序遍历。
无向图的邻接表存储方式广度优先遍历算法实现
// 邻接表的广度遍历操作
void BFSTraverse(MLGraph G) {
Queue q;
EdgeNode *p;
InitQueue(&q);
for(int i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
for(int i = 0; i < G.vertexNum; i++) {
if(!visited[i]) {
// 访问顶点,这里只是简单打印
printf("%c ",G.adjList[i].data);
// 设置访问过后的标记
visited[i] = true;
// 将顶点的下标入队列
EnQueue(&q,i);
// 队列不为空
while(!QueueEmpty(q)) {
// 出队列
DeQueue(&q, &i);
// 将i结点的第一条边赋值给指针p
p = G.adjList[i].firstedge;
// 循环判断边结点是否存在
while(p) {
// 判断邻接点是否被访问过
if(!visited[p->adjvex]) {
printf("%c ",G.adjList[p->adjvex].data);
// 修改邻接顶点的访问标志
visited[p->adjvex] = true;
// 邻接点的下标入队列
EnQueue(&q, p->adjvex);
}
// 指向下一个边表结点
p = p->next;
}
}
}
}
}
邻接矩阵存储方式 :
/* 邻接矩阵的广度遍历算法 */
void BFSTraverse(MGraph G) {
int i, j;
Queue Q;
for(i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
/* 初始化一辅助用的队列 */
InitQueue(&Q);
/* 对每一个顶点做循环 */
for(i = 0; i < G.vertexNum; i++) {
/* 若是未访问过就处理 */
if (!visited[i]) {
/* 设置当前顶点访问过 */
visited[i]=TRUE;
/* 操作顶点,此处只是简单打印 */
printf("%c ", G.vexs[i]);
/* 将此顶点下标入队列 */
EnQueue(&Q,i);
/* 若当前队列不为空 */
while(!QueueEmpty(Q)) {
/* 将队对元素出队列,赋值给i */
DeQueue(&Q,&i);
for(j = 0; j < G.vertexNum; j++) {
/* 判断其它顶点若与当前顶点存在边且未访问过 */
if(G.arc[i][j] == 1 && !visited[j]) {
/* 将找到的此顶点标记为已访问 */
visited[j] = true;
/* 打印顶点 */
printf("%c ", G.vexs[j]);
/* 将找到的此顶点入队列 */
EnQueue(&Q,j);
}
}
}
}
}
}
用到的辅助队列:
Status InitQueue(Queue *Q) {
Q->front = 0;
Q->rear = 0;
return OK;
}
/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
bool QueueEmpty(Queue Q) {
if(Q.front == Q.rear) {
return true;
} else {
return false;
}
}
/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(Queue *Q, int e) {
/* 队列满的判断 */
if ((Q->rear+1)%MAXSIZE == Q->front) {
return ERROR;
}
/* 将元素e赋值给队尾 */
Q->data[Q->rear]=e;
/* rear指针向后移一位置,若到最后则转到数组头部 */
Q->rear=(Q->rear+1)%MAXSIZE;
return OK;
}
/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e) {
/* 队列空的判断 */
if (Q->front == Q->rear) {
return ERROR;
}
/* 将队头元素赋值给e */
*e=Q->data[Q->front];
/* front指针向后移一位置, 若到最后则转到数组头部*/
Q->front=(Q->front+1)%MAXSIZE;
return OK;
}
运行结果:
对比两种遍历算法,它们在时间复杂度上是一样的。