概述
遍历分为深度优先遍历和广度优先遍历,其对有向图和无向图都适用。深度优先,顾名思义,就是只要存在后续节点就一直往下走,直到没有后续节点或者后续节点已经被访问输出了;广度优先,就是节点一层一层的输出。举个例子,
上面的两个图,深度优先遍历的结果为ABCD;而广度优先遍历的结果为ABDC。
如何存储上面的图结构呢?可以采用数组表示法和邻接法。我这里只介绍数组表示法。
图的结构体定义如下:
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM]; //顶点向量
VRType arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵
int vexnum,arcnum; //顶点数和弧数
GKind kind; //图的种类标识
}MGraph;
要对图进行遍历,首先应该把图建立起来。以建立无向图为例。
输入:定点数、边数、定点标号、边的长度。
具体的创建代码如下:
//创建无向图
void CreatUDG(MGraph &G)
{
int i,j,k,w;
char v1,v2;
printf("输入顶点数和边数:");
scanf("%d%d",&G.vexnum,&G.arcnum);
visited = (int *)malloc(G.vexnum*sizeof(int));
for ( i = 0; i < G.vexnum; i++ )
visited[i] = 0;
printf("请按次序输入%d个顶点字母标号(如ABC等):",G.vexnum);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; i++ )
scanf("%c",&G.vexs[i]);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; ++i )
for ( j = 0; j < G.vexnum; ++j )
{//初始化邻接矩阵
if ( i == j )
G.arcs[i][j] = 0;
else
G.arcs[i][j] = INT_MAX;
}
printf("输入边的顶点和权值(如A B 1):\n");
for ( k = 0; k < G.arcnum; k++ )
{
scanf("%c %c %d",&v1,&v2,&w);
i = LocateVex(G,v1);
j = LocateVex(G,v2);
G.arcs[i][j] = w;
G.arcs[j][i] = w;
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
}
}
源代码
以下是遍历的完整代码:
//myGraph.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_VERTEX_NUM 20
typedef int QElemType;
typedef int VRType;
typedef char VertexType;
typedef enum {DG,UDG} GKind; //{有向图,无向图}
typedef struct QNode
{
QElemType data;
struct QNode *next;
}QNode,*QPtr;
typedef struct
{
QPtr front;
QPtr tail;
}LinkQueue;
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM]; //顶点向量
VRType arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵
int vexnum,arcnum; //顶点数和弧数
GKind kind; //图的种类标识
}MGraph;
//初始化队列
void InitQ(LinkQueue &Q);
//从队尾插入元素
void EnQueue(LinkQueue &Q,QElemType e);
//从对首删除元素
void DeQueue(LinkQueue &Q,QElemType &e);
//队列是否为空,为空返回1,否则返回0
int QEmpty(const LinkQueue &Q);
//选择创建图的类型
void CreatGraph(MGraph &G);
//创建无向图
void CreatUDG(MGraph &G);
//创建有向图
void CreatDG(MGraph &G);
//打印邻接矩阵
void printArcs(const MGraph &G);
//返回字符在定点向量中的下标值
int LocateVex(const MGraph &G,VertexType ch);
//深度优先遍历函数
void DFSTraverse(const MGraph &G);
void DFS(const MGraph &G,int v);
//广度优先遍历函数
void BFSTraverse(const MGraph &G);
//得到第一个未被访问的相邻节点下标,若无,则返回-1
int firstAjdVex(const MGraph &G,int v);
//得到下一个未被访问的相邻节点下标,若无,则返回-1
int nextAjdVex(const MGraph &G,int v,int w);
int *visited; //记录顶点是否被访问
//主函数
int main()
{
MGraph G;
CreatGraph(G);
printf("\n原始连接矩阵:\n");
printArcs(G);
//深度优先遍历结果
printf("\n深度优先遍历:");
DFSTraverse(G);
//广度优先遍历结果
printf("\n广度优先遍历:");
BFSTraverse(G);
printf("\n");
return 0;
}
//初始化队列
void InitQ(LinkQueue &Q)
{
Q.front = Q.tail = (QPtr)malloc(sizeof(struct QNode));
if ( !Q.front )
{
printf("InitQ分配内存出错!\n");
return ;
}
Q.front->next = NULL;
}
//从队尾插入元素
void EnQueue(LinkQueue &Q,QElemType e)
{
QPtr q = (QPtr)malloc(sizeof(struct QNode));
if ( !q )
{
printf("EnQueue分配内存出错!\n");
return ;
}
q->data = e;
q->next = NULL;
Q.tail->next = q; //如果是第一次插入,Q.front->next也指向了q
Q.tail = q;
}
//从对首删除元素
void DeQueue(LinkQueue &Q,QElemType &e)
{
if ( Q.front == Q.tail )
{
printf("DeQueue队列为空,不能删除元素!\n");
return ;
}
QPtr q = Q.front->next;
e = q->data;
Q.front->next = q->next;
if ( Q.tail == q )
Q.tail = Q.front;
free(q);
}
//队列是否为空,为空返回1,否则返回0
int QEmpty(const LinkQueue &Q)
{
if ( Q.front == Q.tail )
{
return 1;
}
else
{
return 0;
}
}
//选择创建图的类型
void CreatGraph(MGraph &G)
{
int k;
printf("---------------------\n创建有向图,输入0;\n创建无\
向图,输入1;\n并按下确认键.\n---------------------\n输入:");
scanf("%d",&k);
switch(k)
{
case 0:
G.kind = DG;
CreatDG(G);
break;
case 1:
G.kind = UDG;
CreatUDG(G);
break;
default:
printf("图类型输入有误!");
break;
}
}
//创建无向图
void CreatUDG(MGraph &G)
{
int i,j,k,w;
char v1,v2;
printf("输入顶点数和边数:");
scanf("%d%d",&G.vexnum,&G.arcnum);
visited = (int *)malloc(G.vexnum*sizeof(int));
for ( i = 0; i < G.vexnum; i++ )
visited[i] = 0;
printf("请按次序输入%d个顶点字母标号(如ABC等):",G.vexnum);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; i++ )
scanf("%c",&G.vexs[i]);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; ++i )
for ( j = 0; j < G.vexnum; ++j )
{//初始化邻接矩阵
if ( i == j )
G.arcs[i][j] = 0;
else
G.arcs[i][j] = INT_MAX;
}
printf("输入边的顶点和权值(如A B 1):\n");
for ( k = 0; k < G.arcnum; k++ )
{
scanf("%c %c %d",&v1,&v2,&w);
i = LocateVex(G,v1);
j = LocateVex(G,v2);
G.arcs[i][j] = w;
G.arcs[j][i] = w;
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
}
}
//创建有向图
void CreatDG(MGraph &G)
{//和创建无向图类似,只是少了一句将G.arcs[j][i] = w;
//因为无向图是对称的,而有向图不是
int i,j,k,w;
char v1,v2;
printf("输入顶点数和边数:");
scanf("%d%d",&G.vexnum,&G.arcnum);
visited = (int *)malloc(G.vexnum*sizeof(int));
for ( i = 0; i < G.vexnum; i++ )
visited[i] = 0;
printf("请按次序输入%d个顶点字母标号(如ABC等):",G.vexnum);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; i++ )
scanf("%c",&G.vexs[i]);
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
for ( i = 0; i < G.vexnum; ++i )
for ( j = 0; j < G.vexnum; ++j )
{//初始化邻接矩阵
if ( i == j )
G.arcs[i][j] = 0;
else
G.arcs[i][j] = INT_MAX;
}
printf("输入边的顶点和权值(如A B 1):\n");
for ( k = 0; k < G.arcnum; k++ )
{
scanf("%c %c %d",&v1,&v2,&w);
i = LocateVex(G,v1);
j = LocateVex(G,v2);
G.arcs[i][j] = w;
getchar();//弹出缓冲区中上次最后出入的换行符,即最后按下的回车键
}
}
//打印邻接矩阵
void printArcs(const MGraph &G)
{
int i;
int j;
for ( i = 0; i < G.vexnum; i++ )
{
for ( j = 0; j < G.vexnum; j++ )
{
if ( INT_MAX == G.arcs[i][j])
printf("%6s%","INF");
else
printf("%6d",G.arcs[i][j]);
}
printf("\n");
}
}
//返回字符在定点向量中的下标值
int LocateVex(const MGraph &G,VertexType ch)
{
int i;
for ( i = 0; G.vexnum; i++ )
if ( ch == G.vexs[i] )
return i;
return -1;
}
//深度优先遍历函数
void DFSTraverse(const MGraph &G)
{
int i;
for ( i = 0; i < G.vexnum; i++ )
visited[i] = 0; //初始化访问标记数组
for ( i = 0; i < G.vexnum; i++ )
if ( !visited[i] )
{//对尚未访问的顶点调用DFS
DFS(G,i);
}
printf("\n");
}
void DFS(const MGraph &G,int v)
{
int w;
visited[v] = 1;
printf("%-4c",G.vexs[v]);
for ( w = firstAjdVex(G,v); w >= 0; w = nextAjdVex(G,v,w) )
if ( !visited[w] )
DFS(G,w);
}
//广度优先遍历函数
void BFSTraverse(const MGraph &G)
{
int i,w,v;
LinkQueue Q;
InitQ(Q);
for ( i = 0; i < G.vexnum; i++ )
visited[i] = 0; //初始化访问标记数组
for ( i = 0; i < G.vexnum; i++ )
{
if ( !visited[i] )
{
visited[i] = 1;
printf("%-4c",G.vexs[i]);
EnQueue(Q,i);
while (!QEmpty(Q))
{
DeQueue(Q,v);
for ( w = firstAjdVex(G,v); w >= 0; w = nextAjdVex(G,v,w) )
{
if ( !visited[w] )
{
visited[w] = 1;
printf("%-4c",G.vexs[w]);
EnQueue(Q,w);
}
}
}
}
}
}
//得到第一个未被访问的相邻节点下标,若无,则返回-1
int firstAjdVex(const MGraph &G,int v)
{
int i;
for ( i = 0; i < G.vexnum; i++ )
{
if ( !visited[i] && G.arcs[v][i] > 0 && G.arcs[v][i] < INT_MAX)
return i;
}
return -1;
}
//得到下一个未被访问的相邻节点下标,若无,则返回-1
int nextAjdVex(const MGraph &G,int v,int w)
{
int i;
for ( i = w; i < G.vexnum; i++ )
{
if ( !visited[i] && G.arcs[v][i] > 0 && G.arcs[v][i] < INT_MAX)
return i;
}
return -1;
}
程序运行结果(参照上面给出的例子):