图和图的遍历

来源:互联网 发布:java 开源状态机 编辑:程序博客网 时间:2024/05/18 02:17

由于本人没学过离散数学,在这里只能班门弄斧一下了:

一幅图(graph)G = (V,E)是由顶点集V(vertex)和边集E(egge)组成的。每一条边就是一个点对(v,w),其中v,w∈V。如果点对是有序的,那么图就是有向的,否则成为无向图。如果两个顶点(v1,v2)∈E,那么它们就是相关联的,它们互为邻接点。一个点的度数,指的是与这个点相关联的顶点的个数。对于有向图,还分为入度和出度。如果有一点序列(v1,v2,……vn),如果其中相邻的两个(vi,vi+1)∈E,则它们构成路径。如果v1=vn,则称为环或者回路。如果能从点v1沿着路径到v2,那就称这两点是联通的。如果图中的任意两点都是联通的,那么就称为连通图。如果有令一幅图G'=(V',E'),且V'∈V,E'∈E,则称G'是G的子图。特别的如果两幅图的顶点完全相同,只是第二幅图的边集是第一幅的子集,则第二幅图称为第一幅图的生成子图。

图的基本概念先说这么多吧,其实画出来以后这些概念都很明了的。

那么这种复杂的数据结构,即有点,又有边,该如何表示呢?人们想出了有很多种表示法:邻接矩阵,邻接表等等。这里只介绍最简单的邻接矩阵:就是把图中的n个顶点写成一个n*n的矩阵,如果两个顶点是关联的,那么矩阵的值为1,否者为0。

#include <stdio.h>#include <malloc.h>//深度优先搜索需要使用栈#include"stack.h"//广度优先遍历需要使用队列#include "queue.h"#define MAX 8//邻接矩阵typedef int** adjacentMatrix;//图的数据结构typedef struct graph{//邻接矩阵adjacentMatrix matrix;//顶点向量char* vextex;//顶点向量个数int vextexNum;//边的个数int arcNum;}Graph;//图的基本操作//初始化图Graph initGraph(int );//销毁图void destroyGraph(Graph* );//增加一条边bool addArc(Graph* ,char ,char );//删除一条边bool deleteArc(Graph* ,char ,char );//显示图void showGraph(Graph* );//图的遍历://深度优先搜索的递归实现void do_DFS(Graph* ,int );void DFS(Graph* );//深度优先搜索的栈实现int* findAdjacentVertex(Graph* ,int ,int *);void DFS_byStack(Graph*,char);//广度优先搜索的队列实现void BFS_byQueue(Graph*,char);


 一种数据结构,最重要的操作就是对他遍历。对于图的遍历,有两种常用的方法:深度优先遍历和广度优先遍历。深度优先遍历类似于对二叉树的先序遍历;广度优先遍历类似按层遍历树。

广度优先遍历的基本步骤如下:

1.将初始节点入队

2.当队列不为空时,出队;出队的节点放在vx中。

3.找到与vx相邻且没有被访问过的节点,将它们入队。

4.转2.

注意在每次出队以后,就可以进行相应的操作了,比如打印。

深度优先遍历的基本步骤如下:

1.将起始节点压栈

2.如果栈不为空,弹栈;弹出的节点放在vx中。

3.找到与vx相邻且没被访问过的节点,将它们压栈。

4.转2.

 

下面给出相应的代码:

#include "graph.h"//图的基本操作//初始化图Graph initGraph(int n){Graph g;g.vextexNum = n;g.arcNum = 0;g.matrix = (int**)malloc(sizeof(int*) * n);for(int i = 0; i < n;++i)g.matrix[i] = (int*)malloc(sizeof(int) * n);for(int i = 0;i < n;++i)for(int j = 0; j < n;++j)g.matrix[i][j] = 0;g.vextex = (char*)malloc(sizeof(char) * n);char a = 'A';for(int i = 0; i < n; ++i)g.vextex[i] = a+i;return g;}//销毁图void destroyGraph(Graph* g){free(g->vextex);g->vextex = NULL;for(int i = 0; i < g->vextexNum;++i)free(g->matrix[i]);free(g->matrix);g->matrix = NULL;g->arcNum = -1;g->vextexNum = -1;}//增加一条边bool addArc(Graph* g,char vex1,char vex2){int m = vex1-'A';int n = vex2-'A';if(m < 0 || m > g->vextexNum || n < 0 || n > g->vextexNum ){printf("2 vertexes must in the graph\n");return false;}else{g->matrix[m][n] = 1;g->matrix[n][m] = 1;++g->arcNum;return true;}}//删除一条边bool deleteArc(Graph* g,char vex1,char vex2){int m = vex1-'A';int n = vex2-'A';if(0 == g->matrix[m][n]){printf("this arc does not exsit!\n");return false;}else{g->matrix[m][n] = 0;g->matrix[n][m] = 0;g->arcNum--;return true;}}//显示图void showGraph(Graph* g){printf("  ");for(int i = 0; i < g->vextexNum;++i)printf("%c ",g->vextex[i]);for(int i = 0; i < g->vextexNum;++i){printf("\n");printf("%c ",g->vextex[i]);for(int j = 0;j < g->vextexNum;++j)printf("%d ",g->matrix[i][j]);}printf("\n");}//图的遍历://深度优先搜索的递归实现//需要使用全局变量记录某个顶点是否被访问过bool visited_DFS[MAX];void DFS(Graph* g){printf("使用递归深度优先遍历:\n依次访问节点:");//访问开始前,所有节点均未被访问过for(int i = 0;i < g->vextexNum;++i)visited_DFS[i] = false;//对于每个节点,如果它未被访问,则调用深度优先搜索for(int i = 0; i < g->vextexNum;++i){if(false == visited_DFS[i])do_DFS(g,i);}printf("\n");}void do_DFS(Graph* g,int i){visited_DFS[i] = true;printf("%c ",g->vextex[i]);//依次搜索i的邻接点for(int j = 0; j < g->vextexNum;++j){//如果j是i的邻接点,且未被访问过,则对j调用深度优先搜索if(visited_DFS[j] == false && 1 == g->matrix[i][j])do_DFS(g,j);}}void DFS_byStack(Graph* g,char Start){int start = Start-'A';printf("使用队列进行深度优先遍历:\n依次访问节点:");//建立数组来标记节点是否被访问过bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i)is_visited[i] = false;//建立一个栈Stack s;s = initStack(g->vextexNum);push(&s,start);is_visited[start] = true;while(!is_empty(&s)){int vertex;pop(&s,&vertex);int adjacentNumber;int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);for(int i = 0;i < adjacentNumber;++i){if(false == is_visited[adjacentVertex[i]]){push(&s,adjacentVertex[i]);is_visited[adjacentVertex[i]] = true;}}printf("%c ",g->vextex[vertex]);}printf("\n");destroyStack(&s);free(is_visited);}//寻找与vex相邻的节点,通过n带出结点个数,返回这些节点组成的数组int* findAdjacentVertex(Graph* g,int vex,int *n){//计数器int cnt = 0;int* adj = (int*)malloc(sizeof(int) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i){if(1 == g->matrix[vex][i])adj[cnt++] = i;}*n = cnt;return adj;}void BFS_byQueue(Graph *g,char Start){int start = Start-'A';printf("使用栈进行广度优先遍历:\n依次访问节点:");//建立数组来标记节点是否被访问过bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i)is_visited[i] = false;Queue q;q = initQueue(g->vextexNum);enqueue(&q,&start);is_visited[start] = true;while(!is_empty(&q)){int vertex;dequeue(&q,&vertex);int adjacentNumber;int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);for(int i = 0; i < adjacentNumber;++i){if(false == is_visited[adjacentVertex[i]] ){enqueue(&q,&adjacentVertex[i]);is_visited[adjacentVertex[i]] = true;}}printf("%c ",g->vextex[vertex]);}free(is_visited);destroyQueue(&q);}


注意到,图的深度优先遍历既可以使用栈来实现,也可以使用对归来实现。这与我们之前的认识是相一致的。

程序中使用的栈和队列的代码就不往出贴了,之前的博客都介绍过。主要区别在于这次是通过函数的返回值来获得一个栈或者队列,以前是把指向它们的指针传到函数里。但是二者的思想是大同小异的。

原创粉丝点击