计算机工程系课程设计报告课程名称: 数据结构课程设计题目:图的深度优先遍历、广度遍历优先算法2016年12月20日班 级: 计算机科学与技术15-3班姓 名: 蔺亚楠 学 号: 15091317 指导老师:范智华目录一、设计目的及要求............................................................................................... I I1.题目与设计要求........................................................................................ I I2.本程序涉及的知识点................................................................................ I I二、功能设计........................................................................................................... I I1.总体设计.................................................................................................... I I2.详细设计 (III)三、程序实现 (IV)1.程序实现时应考虑的问题 (IV)2.核心代码 (IV)3.全部代码 (VI)四、测试分析........................................................................................................ X V1.测试结果及分析..................................................................................... X V2.运行结果................................................................................................. X V五、总结 (XVI)六、参考文献 (XVII)一、设计目的及要求1.题目与设计要求本程序要实现的基本功能有:a)以邻接矩阵为存储结构,实现无向图的深度优先遍历和广度优先遍历。
b)分别输出每种遍历下的结点访问序列.从图中某个顶点出发,沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问。
它是许多图的算法的基础。
2.本程序涉及的知识点图的定义、邻阵矩阵表示法、深度优先搜索、广度优先搜索、结构体的定义以及其他相关函数。
二、功能设计1.总体设计数据类型及函数定义定义图typedef struct{int V[M];int R[M][M];int vexnum;}Graph;创建图void creatgraph(Graph *g,int n)打印图的邻接矩阵void printgraph(Graph *g)访问顶点void visitvex(Graph *g,int vex)深度递归遍历void dfs(Graph *g,int vex)队列的基本操作定义队列typedef struct{int V[M];int front;int rear;}Queue;判断队列是否为空quempty(Queue *q)入队操作enqueue(Queue *q,int e)出队操作dequeue(Queue *q)广度遍历void BESTraverse(Graph *g)主程序模块void main (){构造一个图;打印图的邻接矩阵进行深度优先遍历图;进行广度优先遍历图;}2.详细设计1)主函数:主函数一般设计的较为简洁,只提供输入、功能处理和输出部分的函数调用。
2)广度优先遍历函数:基本思想是首先访问图中某一指定的出发点Vi;然后依次访问Vi的所有接点Vi1,Vi2...Vit;再次访问Vi1,Vi2 (Vi)的邻接点中未经访问过的顶点,依此类推,直到图中所有顶点均被访问为止。
3)深度优先遍历函数:基本思想是首先访问图中某一个指定的出发点Vi;然后任选一个与顶点Vi相邻的未被访问过的顶点Vj;以Vj为新的出发点继续进行深度优先搜索,直至图中所有顶点均被访问过。
三、程序实现1.程序实现时应考虑的问题存在的问题:图中可能存在回路,且图的任一顶点都可能与其他顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点解决办法:为了避免重复访问,可设置一个标志顶点是否被访问过的辅助标志visitvex辅助数组visitvex 的初始状态为0,在图的遍历过程中,一旦此顶点被访问,就立刻让visitvex为1,防止它被多次访问Visitevex[0..n-1]的设置:图中任一顶点都可能和其它顶点相邻接。
在访问了某顶点之后,又可能顺着某条回路又回到了该顶点。
为了避免重复访问同一个顶点,必须记住每个已访问的顶点。
为此,可设一布尔向量visitvex[0..n-1],其初值为假,一旦访问了顶点Vi之后,便将visitvex[i]置为真。
2.核心代码1)以邻接矩阵为存储结构的图的深度优先搜索遍历的算法void dfs(Graph *g,int vex){ int w;visited[vex]=1; 标记vex已被访问过visitvex(g,vex); 访问图g的顶点for(w=firstadjvex(g,vex);w>0;w=nextadjvex(g,vex,w))if(!visited[w]){ dfs(g,w); } }从初始点vex出发广度优先搜索由邻接矩阵R表示的图void dfstraverse(Graph *g){ int i;for(i=1;i<=g->vexnum;i++)visited[i]=0;for(i=1;i<=g->vexnum;i++)if(!visited[i]){dfs(g,i);} }2)以邻接矩阵为存储结构的图的广度优先搜索遍历的算法从初始点vex出发广度优先搜索由邻接矩阵R表示的图void BESTraverse(Graph *g){ int i;Queue *q=(Queue *)malloc(sizeof(Queue)); 定义一个队列q,其元素类型应为Queue型for(i=1;i<=g->vexnum;i++){ visited[i]=0; 标记初始点i未被已访问过}initqueue(q); 初始化队列for(i=1;i<=g->vexnum;i++){ if(!visited[i]){ visited[i]=1; 标记初始点i已访问过visitvex(g,g->V[i]); 访问顶点ienqueue(q,g->V[i]); 将已访问过的初始点序号i入队while(!quempty(q)) 当队列非空时进行循环处理{依次搜索i的每一个可能的邻接点int u,w;u=dequeue(q);for(w=firstadjvex(g,u);w>0;w=nextadjvex(g,u,w)){ if(!visited[w]){ visited[w]=1; 访问一个未被访问过的邻接点visitvex(g,w); 访问顶点wenqueue(q,w); 顶点w出队}}}} } }3.全部代码#include <stdio.h>#include <malloc.h>#define INF 32767 /*INF表示∞*/typedef int InfoType;#define MAXV 100 /*最大顶点个数*/#define MAXQUEUE 10 /* 队列的最大容量*/struct node /* 图的顶点结构定义*/{int vertex; struct node *nextnode;};typedef struct node *graph; /* 图的结构指针*/struct node head[9]; /* 图的顶点数组*/int visit[9]; /* 遍历标记数组*/int queue[MAXQUEUE]; /* 定义序列数组*/int front = -1; /* 序列前端*/int rear = -1; /* 序列后端*//***********************二维数组向邻接表的转化****************************/void creategraph(int node[20][2], int num){graph newnode; /* 顶点指针*/graph ptr;int from; /* 边起点*/int to; /* 边终点*/int i;for (i = 0; i < num; i++) /* 第i条边的信息处理*/{from = node[i][0]; /* 边的起点*/to = node[i][1]; /* 边的终点*//* 建立新顶点*/newnode = (graph)malloc(sizeof(struct node));newnode->vertex = to; /* 顶点内容*/newnode->nextnode = NULL; /* 设定指针初值*/ptr = &(head[from]); /* 顶点位置*/while (ptr->nextnode != NULL) /* 遍历至链表尾*/ptr = ptr->nextnode; /* 下一个顶点*/ ptr->nextnode = newnode; /* 插入第i个节点的链表尾部*/ }}/************************ 数值入队列************************************/int enqueue(int value){if (rear >= MAXQUEUE) /* 检查伫列是否全满*/ return -1; /* 无法存入*/ rear++; /* 后端指标往前移*/queue[rear] = value; /* 存入伫列*/return 0;}/************************* 数值出队列*********************************/int dequeue(){if (front == rear) /* 队列是否为空*/return -1; /* 为空,无法取出*/ front++; /* 前端指标往前移*/return queue[front]; /* 从队列中取出信息*/}/*********************** 图形的广度优先遍历************************/void bfs(int current){graph ptr;/* 处理第一个顶点*/enqueue(current); /* 将顶点存入队列*/visit[current] = 1; /* 已遍历过记录标志置疑1*/printf(" Vertex[%d]\n", current); /* 打印输出遍历顶点值*/while (front != rear) /* 队列是否为空*/{current = dequeue(); /* 将顶点从队列列取出*/ptr = head[current].nextnode; /* 顶点位置*/while (ptr != NULL) /* 遍历至链表尾*/{if (visit[ptr->vertex] == 0) /*顶点没有遍历过*/{enqueue(ptr->vertex); /* 将定点放入队列*/visit[ptr->vertex] = 1; /* 置遍历标记为1 */printf(" Vertex[%d]\n", ptr->vertex);/* 印出遍历顶点值*/ }ptr = ptr->nextnode; /* 下一个顶点*/}}}/*以下定义邻接矩阵类型*/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和边数e*/} ALGraph; /*图的邻接表类型*/int visited[MAXV];void MatToList(MGraph g, ALGraph *&G)/*将邻接矩阵g转换成邻接表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] != INF) /*邻接矩阵的当前元素不为0*/{p = (ArcNode *)malloc(sizeof(ArcNode)); /*创建一个结点*p*/p->adjvex = j;p->info = g.edges[i][j];p->nextarc = G->adjlist[i].firstarc;G->adjlist[i].firstarc = p;}G->n = n; G->e = g.arcnum;}void ListToMat(ALGraph *G, MGraph &g)/*将邻接表G转换成邻接矩阵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] = INF;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;}void DispMat(MGraph g)/*输出邻接矩阵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");}}void DispAdj(ALGraph *G)/*输出邻接表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");}}void DFS(ALGraph *G, int v){ArcNode *p; visited[v] = 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){int i, visited[MAXV], St[MAXV], top = -1;ArcNode *p;for (i = 0; i<G->n; i++)visited[i] = 0; /*结点访问标志均置成0*/ printf("%3d", v); /*访问顶点v*/top++; /*v入栈*/St[top] = v;visited[v] = 1;while (top >= -1) /*栈不空时循环*/{v = St[top]; top--; /*取栈顶顶点*/p = G->adjlist[v].firstarc;while (p != NULL && visited[p->adjvex] == 1)p = p->nextarc; if (p == NULL) /*若没有退到前一个*/ top--;else /*若有,选择一个*/{v = p->adjvex;printf("%3d", v); /*访问顶点*/visited[v] = 1;top++; /*入栈*/St[top] = v;}}printf("\n");}int main(){int i, j;MGraph g, g1;ALGraph *G;graph ptr;/* 边信息数组*/int node[20][2] = { { 1, 2 }, { 2, 1 }, { 6, 3 }, { 3, 6 }, { 2, 4 }, { 4, 2 },{1, 5}, { 5, 1 }, { 3, 7 }, { 7, 3 }, { 1, 7 }, { 7, 1 }, { 4, 8 }, { 8, 4 }, { 5, 8 }, { 8, 5 }, { 2, 8 }, { 8, 2 }, { 7, 8 }, { 8, 7 } };int A[MAXV][6] = { { INF, 5, INF, 7, INF, INF }, { INF, INF, 4, INF, INF, INF }, { 8, INF, INF, INF, INF, 9 }, { INF, INF, 5, INF, INF, 6 }, { INF, INF, INF, 5, INF, INF }, { 3, INF, INF, INF, 1, INF } };g.vexnum = 6; g.arcnum = 10;puts("This is an example of Width Preferred Traverse of Gragh.\n");for (i = 1; i <= 8; i++) /*顶点结构数组初始化*/{head[i].vertex = i; head[i].nextnode = NULL; visit[i] = 0;}creategraph(node, 20); /* 图信息转换,邻接表的建立*/ printf("The content of the graph's allist is:\n");for (i = 1; i <= 8; i++){printf(" vertex%d =>", head[i].vertex); /* 顶点值*/ptr = head[i].nextnode; /* 顶点位置*/while (ptr != NULL) /* 遍历至链表尾*/{printf(" %d ", ptr->vertex); /* 打印输出顶点内容*/ptr = ptr->nextnode; /* 下一个顶点*/ }printf("\n"); /* 换行*/}printf("The contents of BFS are:\n");bfs(1); /* 打印输出遍历过程*/ printf("\n"); /* 换行*/ puts(" Press any key to quit..."); getchar();for (i = 0; i<g.vexnum; i++) /*建立图9.1的邻接矩阵*/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); printf("图G的邻接表:\n");DispAdj(G); printf(" 图G的邻接表转换成邻接邻阵:\n");ListToMat(G, g1); DispMat(g1); printf("\n");printf("从顶点0开始的DFS(递归算法):\n");DFS(G, 0); printf("\n");printf("从顶点0开始的DFS(非递归算法):\n");DFS1(G, 0); printf("\n");}四、测试分析1.测试结果及分析调试过程中遇到的问题以及对设计与实现的回顾讨论和分析:一个图的DFS序列不一定惟一:当从某顶点x出发搜索时,若x的邻接点有多个尚未访问过,则我们可任选一个访问之;源点和存储结构的内容均已确定的图的DFS序列惟一;邻接矩阵表示的图确定源点后,DFS序列惟一;DFS算法中,当从vi出发搜索时,是在邻接矩阵的第i行上从左至右选择下一个未曾访问过的邻接点作为新的出发点,若这样的邻接点多于一个,则选中的总是序号较小的那一个。