建立一个有向图或无向图,输入其顶点数,边数,并给出相应边的权值,输出该图对应的邻接矩阵,并用递归实现其深度优先遍历和用队列实现其广度优先遍历后的结果.
图的遍历
从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历
图的遍历方法有两种,一种叫深度优先遍历(DFS),另一种叫广度优先遍历(BFS).
图的邻接矩阵表示
通常图的表示有两种方法:邻接矩阵,邻接表。
本文用邻接矩阵实现,一是代码量更少,二是代码风格也更贴近C语言。但不论是图的哪种实现方式,其基本的实现思想是不变的。
1:节点的信息,我们用一维数组a[n]来存储,假设图共有n个节点。
2:节点与节点间的关系,我们用二维数组b[n][n]存储。
3:b[i][j]表示,从i到j有向连通,b[j][i]表示从j到i有向连通,而当i=j时(矩阵的对角线上的元素),b[i][j]没有实际意思,在遍历时,我可以用来存储定节点是否访问过。
深度优先遍历(DFS)
图的深度优先搜索(Depth First Search),和树的先序遍历比较类似。
它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
显然,深度优先搜索是一个递归的过程
深度优先遍历特点是,选定一个出发点后进行遍历,能前进则前进,若不能前进,回退一步再前进,或再回退一步后继续前进。依此重复,直到所有与选定点相通的所有顶点都被遍历。
广度优先遍历(BFS)
广度优先遍历的过程是首先访问初始点v,接着访问顶点v的所有未被访问过的邻接点v1,v2,…,vt,然后再按照v1,v2,…,vt的次序访问每一个顶点的所有未被访问过的邻接点,以此类推,直到图中所有和初始点v有路径相通的顶点都被访问过为止。
在遍历以邻接表为存储结构的图时,需要使用一个队列,
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 100 //最大定点顶点数
#define INFINITF 65535 //用 65535来表示无穷
#define MAX 100
#define MAXSIZE 100
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Boolean; //Boolean是布尔类型,其值是TRUE或FALSE
Boolean visited[MAX];
typedef struct{
char vexs[MAXVEX]; //顶点表
int arc[MAXVEX][MAXVEX]; //邻接矩阵,可看作边表
int numVertexes,numEdges; //图中当前的顶点数
int GraphType; //图的类型
}MGraph;
typedef struct{
int data[MAXSIZE];
int front; //头指针
int rear; //尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
//初始化一个空队列
int InitQueue(SqQueue *Q)
{
Q->front=0;
Q->rear=0;
return OK;
}
int QueueEmpty(SqQueue Q)
{
if(Q.rear==Q.front)
return TRUE;
else
return FALSE;
}
//循环队列入队列操作
int EnQueue(SqQueue *Q,int e)
{
if ((Q->rear+1)%MAXSIZE == Q->front); //队列满的判断
return ERROR;
Q->data[Q->rear]=e; //将元素e赋值给队尾
Q->rear=(Q->rear+1)%MAXSIZE; //rear指针向后移一位置,若到最后则转到数组头部
return OK;
}
// 循环队列出队列操作
int DeQueue(SqQueue *Q,int *e)
{
if (Q->front == Q->rear)
return ERROR; //队列空的判断
*e = Q->data[Q->front]; //将队头元素赋值给e
Q->front=(Q->front+1)%MAXSIZE; //front指针向后移一位置
return OK;
}
void CreateMGraph (MGraph *G)
{
int i,j,k,w;
printf("输入顶点数和边数:\n");
scanf("%d %d",&G->numVertexes,&G->numEdges); //输入顶点数和边数
fflush(stdin);
for(i=0;i<G->numVertexes;i++)
{
printf("第%d个顶点",i+1);
scanf("%c",&G->vexs[i]);
getchar ();
}
for(i=0;i<G->numVertexes;i++)
for(j=0;j<G->numVertexes;j++)
G->arc[i][j]=INFINITF ; //邻接矩阵初始化
for(k=0;k<G->numEdges;k++)
{
printf("输入边(vi,vj)上的上标i,下标j和权W:");
scanf("%d %d %d",&i,&j,&w); //输入边(vi,vj)上的权W
G->arc[i][j]=w;
if(G->GraphType==0)
G->arc[j][i]=G->arc[i][j]; //因为是无向图,矩阵对称
}
}
//输出邻接矩阵
void output(MGraph *G)
{
int i,j,count=0;
for (i=0;i<G->numVertexes;i++)
printf("\t%c",G->vexs[i]);
printf("\n");
for(i=0;i<G->numVertexes;i++)
{
printf("%4c",G->vexs[i]);
for(j=0;j<G->numVertexes;j++)
{
printf("\t%d",G->arc[i][j]);
count++;
if(count%G->numVertexes==0)
printf("\n");
}
}
}
//邻接矩阵的深度优先递归算法
void DFS (MGraph G,int i)
{
int j;
visited[i]=TRUE;
printf("%c ",G.vexs[i]); //打印顶点
for(j=0;j<G.numVertexes;j++)
{
if(G.arc[i][j]==1&&!visited[j])
DFS(G,j); //对未访问的邻接顶点递归调用
}
}
//邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G)
{
int i;
for(i=0;i<G.numVertexes;i++)
visited[i]=FALSE; //初始化所有顶点状态都是未访问过状态
for(i=0;i<G.numVertexes;i++)
if(!visited[i]) //对未访问过的顶点调用DFS,若是连通图,只会执行一次
DFS(G,i);
}
void BFSTraverse(MGraph G)
{
int i,j;
SqQueue Q;
for(i=0;i<G.numVertexes;i++)
visited[i]=FALSE;
InitQueue(&Q); //初始化一辅助用的队列
for(i=0;i<G.numVertexes;i++) //对每个顶点做循环
{
if(!visited[i]) //若是未访问过就处理
{
visited[i]=TRUE; //设置当前顶点访问过
printf("%c ",G.vexs[i]); //打印顶点
EnQueue(&Q,i); //将此顶点入队列
while(!QueueEmpty(Q)) //若当前队列不为空
{
DeQueue(&Q,&i); //将队中元素出队列,赋值给i
for(j=0;j<G.numVertexes;j++)
{
//判断其他结点若与当前顶点存在且未访问过
if(G.arc[i][j] == 1&& !visited[j])
{
visited[j]=TRUE; //将找到的此顶点标记为已访问
printf("%c ",G.vexs[j]); //打印顶点
EnQueue(&Q,j); //将找到的此顶点入队列
}
}
}
}
}
}
int main()
{
MGraph G;
int i,j;
printf("输入图的类型(无向图0/有向图1):");
scanf("%d",&G.GraphType);
CreateMGraph (&G);
printf("邻接矩阵数据如下:\n");
output(&G);
printf("\n");
printf("图的深度优先遍历如下:\n");
DFSTraverse(G);
printf("\n图的广度优先遍历如下:\n");
BFSTraverse(G);
return 0;
}