连通图里的深度优先和广度优先遍历
2015-10-05 21:31
471 查看
从图中的某个顶点出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使得每个顶点仅被访问一次,这个过程称为图的遍历。图的遍历有两种:深度优先遍历和广度优先遍历。
图分为连通图和非连通图,这里主要讨论连通图的深度、广度优先遍历。
一、深度优先遍历
图的深度优先遍历类似于树的先序遍历,它的基本思想是:首先访问指定的起始顶点v, 然后选取与v邻接的未被访问的任意一个顶点w, 访问之,再选取与w邻接的未被访问的任一顶点,访问之。
重复进行如上的访问,当一个顶点所有邻接顶点都被访问过时,则依次退回到最近被访问过的顶点,若它还有邻接顶点未被访问过,则从这些未被访问的顶点中取出一个顶点开始访问,直到所有的顶点都被访问过为止。
以邻接表为存储结构的深度优先遍历算法如下(其中,v是初始顶点编号,visited[]是一个全局数组,初始时所有元素均为1):
二、广度优先遍历
图的广度优先遍历(又叫宽度优先遍历)类似于树的层次遍历,它的基本思想是:首先访问指定的起始顶点v, 然后选取与v邻接的全部顶点w1,w2,⋯wt, 再依次访问与w1,w2,⋯wt邻接的全部顶点(已被访问的顶点除外), 再从这些被访问的顶点出发,逐次访问与它们邻接的全部顶点(已被访问的顶点除外)。依此类推,直到所有的顶点都被访问过为止。
以邻接表为存储结构,用广度优先搜索遍历图时,需要使用一个队列,对应的算法如下:
完整代码如下:
效果如下:
图(1) 有向图G的深度优先和广度优先遍历;邻接表输出时,由于结点3和结点5的出度都为0,所以是空表即对应的单链表不输出,而结点0、1、2和4的出度都大于0,所以有输出
图分为连通图和非连通图,这里主要讨论连通图的深度、广度优先遍历。
一、深度优先遍历
图的深度优先遍历类似于树的先序遍历,它的基本思想是:首先访问指定的起始顶点v, 然后选取与v邻接的未被访问的任意一个顶点w, 访问之,再选取与w邻接的未被访问的任一顶点,访问之。
重复进行如上的访问,当一个顶点所有邻接顶点都被访问过时,则依次退回到最近被访问过的顶点,若它还有邻接顶点未被访问过,则从这些未被访问的顶点中取出一个顶点开始访问,直到所有的顶点都被访问过为止。
以邻接表为存储结构的深度优先遍历算法如下(其中,v是初始顶点编号,visited[]是一个全局数组,初始时所有元素均为1):
int visited[MAXV]; //连通图的深度优先遍历 void DFS(ALGraph *G,int v){ //G指向某个邻接表,v是起始顶点 ArcNode *p; visited[v]=1; //已访问,则置1 printf("%3d",v); //输出被访问顶点的编号 p=G->adjlist[v].firstarc; //p指向顶点v的第一条弧的弧头结点 while (p!=NULL) { if(visited[p->adjvex]==0) //若p->adjvex顶点未访问,递归访问它 DFS(G,p->adjvex); p=p->nextarc; //p指向顶点v的下一条弧的弧头结点 } }
二、广度优先遍历
图的广度优先遍历(又叫宽度优先遍历)类似于树的层次遍历,它的基本思想是:首先访问指定的起始顶点v, 然后选取与v邻接的全部顶点w1,w2,⋯wt, 再依次访问与w1,w2,⋯wt邻接的全部顶点(已被访问的顶点除外), 再从这些被访问的顶点出发,逐次访问与它们邻接的全部顶点(已被访问的顶点除外)。依此类推,直到所有的顶点都被访问过为止。
以邻接表为存储结构,用广度优先搜索遍历图时,需要使用一个队列,对应的算法如下:
//连通图的广度优先遍历 void BFS(ALGraph *G,int v){ //G指向某个邻接表,v是起始顶点 ArcNode *p; int queue[MAXV],front=0,rear=0; //定义循环队列并初始化 int visited[MAXV]; //定义存放结点的访问标志的数组 int w,i; for(i=0;i< G->n;i++) visited[i]=0; //访问标志数组初始化 printf("%3d",v); //输出被访问顶点的编号 visited[v]=1; //置已访问标记 rear=(rear+1)%MAXV; queue[rear]=v; //v进队 while (front!=rear) //若队列不空时循环 { front=(front+1)%MAXV; w=queue[front]; //出队并赋给w p=G->adjlist[w].firstarc; //找与顶点w邻接的第一个顶点 while (p!=NULL) { if (visited[p->adjvex]==0) //若当前邻接顶点未访问 { printf("%3d",p->adjvex); //访问相邻顶点 visited[p->adjvex]=1; //置该顶点已被访问的标志 rear=(rear+1)%MAXV; //该顶点进队 queue[rear]=p->adjvex; } p=p->nextarc; //找下一个邻接顶点 } } printf("\n"); }
完整代码如下:
//GraphBase.h
typedef int InfoType;
#define MAXV 100 //最大顶点个数
//定义邻接矩阵类型
typedef struct{
int no; //顶点编号
InfoType info; //顶点其他信息
}VertexType; //顶点类型
typedef struct{ //图的定义
int edges[MAXV][MAXV]; //邻接矩阵
int vexnum,arcnum; //顶点数,弧数
VertexType vexs[MAXV]; //存放顶点信息
}MGraph; //图的邻接矩阵类型
//定义邻接表类型
typedef struct ANode{ //弧的结点结构类型
int adjvex; //该弧的终点位置
struct ANode *nextarc; //指向下一条弧的指针
InfoType info; //弧的相关信息,用来存放权值
}ArcNode;
typedef int Vertex;
typedef struct Vnode{ //邻接表头结点的类型
Vertex data; //顶点信息
ArcNode *firstarc; //指向第一条弧
}VNode;
typedef VNode AdjList[MAXV]; //AdjList是邻接表类型
typedef struct{
AdjList adjlist; //邻接表
int n,e; //图中顶点数n和边数
}ALGraph; //图的邻接表类型
//主函数.cpp
#include <stdio.h>
#include <malloc.h>
#include "GraphBase.h"
#define INF 32767 //INF表示无穷大,即∞
//将邻接矩阵g转换邻接表G
void MatToList(MGraph g,ALGraph *&G){
int i,j,n=g.vexnum; //n为顶点数
ArcNode *p;
G=(ALGraph *)malloc(sizeof(ALGraph));
for(i=0;i<n;i++) //给邻接表中所有头结点的指针域置初值
G->adjlist[i].firstarc=NULL;
for (i=0;i<n;i++) //检查邻接矩阵中的每个元素
for (j=n-1;j>=0;j--)
if (g.edges[i][j]!=0) //邻接矩阵的当前元素不为0
{
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点*p
p->adjvex=j;
p->info=g.edges[i][j];
p->nextarc=G->adjlist[i].firstarc; //将*p链接到链表后面
G->adjlist[i].firstarc=p;
}
G->n=n; G->e=g.arcnum;
}
//将邻接表G转换为邻接矩阵g
void ListToMat(ALGraph *G,MGraph &g){
int i,j,n=G->n;
ArcNode *p;
for(i=0;i<n;i++) //g.edges[i][j]赋初值0
for(j=0;j<n;j++)
g.edges[i][j]=0;
for (i=0;i<n;i++)
{
p=G->adjlist[i].firstarc;
while(p!=NULL){
g.edges[i][p->adjvex]=p->info;
p=p->nextarc;
}
}
g.vexnum=n; g.arcnum=G->e;
}
//输出邻接矩阵g
void DispMat(MGraph g){
int i,j;
for (i=0;i<g.vexnum;i++)
{
for(j=0;j<g.vexnum;j++)
if(g.edges[i][j]==INF)
printf("%3s","∞");
else
printf("%3d",g.edges[i][j]);
printf("\n");
}
}
//输出邻接表G
void DispAdj(ALGraph *G){
int i;
ArcNode *p;
for (i=0;i< G->n;i++)
{
p=G->adjlist[i].firstarc;
if(p!=NULL) printf("%3d: ",i);
while (p!=NULL)
{
printf("%3d",p->adjvex);
p=p->nextarc;
}
printf("\n");
}
}
int visited[MAXV]; //连通图的深度优先遍历 void DFS(ALGraph *G,int v){ //G指向某个邻接表,v是起始顶点 ArcNode *p; visited[v]=1; //已访问,则置1 printf("%3d",v); //输出被访问顶点的编号 p=G->adjlist[v].firstarc; //p指向顶点v的第一条弧的弧头结点 while (p!=NULL) { if(visited[p->adjvex]==0) //若p->adjvex顶点未访问,递归访问它 DFS(G,p->adjvex); p=p->nextarc; //p指向顶点v的下一条弧的弧头结点 } }
//连通图的深度优先遍历(非递归)
void DFS1(ALGraph *G,int v){
ArcNode *p;
ArcNode *St[MAXV];
int top=-1,w,i;
for(i=0;i< G->n;i++)
visited[i]=0;
printf("%3d",v);
visited[v]=1;
top++;
St[top]=G->adjlist[v].firstarc;
while (top > -1)
{
p=St[top]; top--;
while (p!=NULL)
{
w=p->adjvex;
if (visited[w]==0)
{
printf("%3d",w);
visited[w]=1;
top++;
St[top]=G->adjlist[w].firstarc;
break;
}
p=p->nextarc;
}
}
printf("\n");
}
//连通图的广度优先遍历 void BFS(ALGraph *G,int v){ //G指向某个邻接表,v是起始顶点 ArcNode *p; int queue[MAXV],front=0,rear=0; //定义循环队列并初始化 int visited[MAXV]; //定义存放结点的访问标志的数组 int w,i; for(i=0;i< G->n;i++) visited[i]=0; //访问标志数组初始化 printf("%3d",v); //输出被访问顶点的编号 visited[v]=1; //置已访问标记 rear=(rear+1)%MAXV; queue[rear]=v; //v进队 while (front!=rear) //若队列不空时循环 { front=(front+1)%MAXV; w=queue[front]; //出队并赋给w p=G->adjlist[w].firstarc; //找与顶点w邻接的第一个顶点 while (p!=NULL) { if (visited[p->adjvex]==0) //若当前邻接顶点未访问 { printf("%3d",p->adjvex); //访问相邻顶点 visited[p->adjvex]=1; //置该顶点已被访问的标志 rear=(rear+1)%MAXV; //该顶点进队 queue[rear]=p->adjvex; } p=p->nextarc; //找下一个邻接顶点 } } printf("\n"); }
void main()
{
int i,j;
MGraph g;
ALGraph *G;
int A[6][6]={
{0,5,0,5,5,0},
{0,0,4,0,4,0},
{0,0,0,0,0,9},
{0,0,0,0,0,0},
{0,0,5,7,0,7},
{0,0,0,0,0,0}
};
g.vexnum=6; g.arcnum=10;
for (i=0;i<g.vexnum;i++)
for(j=0;j<g.vexnum;j++)
g.edges[i][j]=A[i][j];
printf("\n");
printf(" 有向图G的邻接矩阵: \n");
DispMat(g);
G=(ALGraph *)malloc(sizeof(ALGraph));
printf(" 图G的邻接矩阵转换为邻接表: \n");
MatToList(g,G);
DispAdj(G);
// printf(" 图G的邻接表转换为邻接矩阵: \n");
// ListToMat(G,g1);
// DispMat(g1);
// printf("\n");
printf("深度优先遍历:\n");
DFS(G,0);
printf("\n");
printf("深度优先遍历:\n");
BFS(G,0);
printf("\n");
}
效果如下:
图(1) 有向图G的深度优先和广度优先遍历;邻接表输出时,由于结点3和结点5的出度都为0,所以是空表即对应的单链表不输出,而结点0、1、2和4的出度都大于0,所以有输出
相关文章推荐
- 文件遍历排序函数
- Lua 学习笔记之C API 遍历 Table实现代码
- C#中遍历Hashtable的4种方法
- Erlang中遍历取出某个位置的最大值代码
- C++实现图的邻接矩阵存储和广度、深度优先遍历实例分析
- C++实现图的邻接表存储和广度优先遍历实例分析
- C++非递归队列实现二叉树的广度优先遍历
- php遍历目录方法小结
- 一个目录遍历函数
- php遍历删除整个目录及文件的方法
- PHP遍历文件夹与文件类及处理类用法实例
- PHP遍历XML文档所有节点的方法
- php中使用key,value,current,next和prev函数遍历数组的方法
- C#使用前序遍历、中序遍历和后序遍历打印二叉树的方法
- C#使用foreach遍历哈希表(hashtable)的方法
- php递归遍历多维数组的方法
- C#使用yield关键字让自定义集合实现foreach遍历的方法
- C#使用foreach语句简单遍历数组的方法
- Javascript遍历Html Table示例(包括内容和属性值)
- JavaScript遍历table表格中的某行某列并打印其值