数据结构—图的遍历(深度优先和广度优先)

在图的结构中常用的遍历方式有两种:深度优先搜索(DFS,也可以叫做深度优先搜索)和广度优先搜索(BFS,也可以叫做深度优先搜索)。

深度优先搜索

深度优先的递归定义

所谓深度优先搜索,是从图中的一个顶点出发,每次遍历当前访问顶点的临界点,一直到访问的顶点没有未被访问过的临界点为止。然后采用依次回退的方式,查看来的路上每一个顶点是否有其它未被访问的临界点。访问完成后,判断图中的顶点是否已经全部遍历完成,如果没有,以未访问的顶点为起始点,重复上述过程。

图 1 无向图.png

深度优先搜索的过程类似于树的先序遍历,我们从例子中体会深度优先搜索。例如图 1 是一个无向图,采用深度优先算法遍历这个图的过程为:

  1. 首先任意找一个未被遍历过的顶点,例如从 V1 开始,由于 V1 率先访问过了,所以,需要标记 V1 的状态为访问过;
  2. 然后遍历 V1 的邻接点,例如访问 V2 ,并做标记,然后访问 V2 的邻接点,例如 V4 (做标记),然后 V8 ,然后 V5 ;
  3. 当继续遍历 V5 的邻接点时,根据之前做的标记显示,所有邻接点都被访问过了。此时,从 V5 回退到 V8 ,看 V8 是否有未被访问过的邻接点,如果没有,继续回退到 V4 , V2 , V1 ;
  4. 通过查看 V1 ,找到一个未被访问过的顶点 V3 ,继续遍历,然后访问 V3 邻接点 V6 ,然后 V7 ;
  5. 由于 V7 没有未被访问的邻接点,所有回退到 V6 ,继续回退至 V3 ,最后到达 V1 ,发现没有未被访问的;
  6. 最后一步需要判断是否所有顶点都被访问,如果还有没被访问的,以未被访问的顶点为第一个顶点,继续依照上边的方式进行遍历。

根据上边的过程,可以得到图 1 通过深度优先搜索获得的顶点的遍历次序为:

V1 -> V2 -> V4 -> V8 -> V5 -> V3 -> V6 -> V7

注意:深度优先搜索是一个不断回溯的过程。

深度优先的实现

关于深度优先实现可以分为两种形式:递归和非递归。这与树中的先序遍历基本没什么差别。

伪代码

1、递归实现

1、访问顶点v:   visited[v]=1; //算法执行前visited[n] = 0; 
2、w = 顶点v的第一个邻接点;
3、while(w存在){
           if(w未被访问)
                   从顶点w出发递归执行该算法; 
           w = 顶点v的下一个邻接点;
}

2、非递归实现

1、 栈S的初始化:  visited[n=0]
2、访问顶点v:  visited[v]=1;  顶点v入栈s
3、while(栈S非空){
          x=栈S的顶元素(不出栈);
          if(存在并找到未曾访问的x邻接点w){
                访问w;  visited[w]=1;
                w入栈;
          }else{
                x出栈;
          }   
}

递归方式实现:

#include <stdio.h>

typedef enum{
    false,true
}bool;               //定义bool型常量
bool visited[20];               //设置全局数组,记录标记顶点是否被访问过

typedef struct {
    int adj;                 //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
    char * info;             //弧或边额外含有的信息指针
}ArcCell,AdjMatrix[20][20];


typedef struct {
    int vexs[20];        //存储图中顶点数据
    AdjMatrix arcs;      //二维数组,记录顶点之间的关系
    int vexnum,arcnum;   //记录图的顶点数和弧(边)数
}MGraph;


//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph * G,int v){
    int i=0;
    //遍历一维数组,找到变量v
    for (; i<G->vexnum; i++) {
        if (G->vexs[i]==v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i>G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//构造无向图
void CreateDN(MGraph *G){
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    int i,j;
    for (i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (i=0; i<G->vexnum; i++) {
        for (j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    for (i=0; i<G->arcnum; i++) {
        int v1,v2;
        scanf("%d,%d",&v1,&v2);
        int n=LocateVex(G, v1);
        int m=LocateVex(G, v2);
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj=1;
        G->arcs[m][n].adj=1;//无向图的二阶矩阵沿主对角线对称
    }
}

int FirstAdjVex(MGraph G,int v)
{
    int i;
    //查找与数组下标为v的顶点之间有边的顶点,返回它在数组中的下标
    for(i = 0; i<G.vexnum; i++){
        if( G.arcs[v][i].adj ){
            return i;
        }
    }
    return -1;
}
int NextAdjVex(MGraph G,int v,int w)
{
    int i;
    //从前一个访问位置w的下一个位置开始,查找之间有边的顶点
    for(i = w+1; i<G.vexnum; i++){
        if(G.arcs[v][i].adj){
            return i;
        }
    }
    return -1;
}
void visitVex(MGraph G, int v){
    printf("%d ",G.vexs[v]);
}
void DFS(MGraph G,int v){
    visited[v] = true;//标记为true
    visitVex( G,  v); //访问第v 个顶点
    //从该顶点的第一个边开始,一直到最后一个边,对处于边另一端的顶点调用DFS函数
    int w;
    for(w = FirstAdjVex(G,v); w>=0; w = NextAdjVex(G,v,w)){
        //如果该顶点的标记位false,证明未被访问,调用深度优先搜索函数
        if(!visited[w]){
            DFS(G,w);
        }
    }
}
//深度优先搜索
void DFSTraverse(MGraph G){//
    int v;
    //将用做标记的visit数组初始化为false
    for( v = 0; v < G.vexnum; ++v){
        visited[v] = false;
    }
    //对于每个标记为false的顶点调用深度优先搜索函数
    for( v = 0; v < G.vexnum; v++){
        //如果该顶点的标记位为false,则调用深度优先搜索函数
        if(!visited[v]){
            DFS( G, v);
        }
    }
}

int main() {
    MGraph G;//建立一个图的变量
    CreateDN(&G);//初始化图
    DFSTraverse(G);//深度优先搜索图
    return 0;
}
执行结果.png

广度优先搜索

广度优先搜索类似于树的层次遍历。从图中的某一顶点出发,遍历每一个顶点时,依次遍历其所有的邻接点,然后再从这些邻接点出发,同样依次访问它们的邻接点。按照此过程,直到图中所有被访问过的顶点的邻接点都被访问到。

最后还需要做的操作就是查看图中是否存在尚未被访问的顶点,若有,则以该顶点为起始点,重复上述遍历的过程。

还拿图 1 中的无向图为例,假设 V1 作为起始点,遍历其所有的邻接点 V2 和 V3 ,以 V2 为起始点,访问邻接点 V4 和 V5 ,以 V3 为起始点,访问邻接点 V6 、 V7 ,以 V4 为起始点访问 V8 ,以 V5 为起始点,由于 V5 所有的起始点已经全部被访问,所有直接略过, V6 和 V7 也是如此。

以 V1 为起始点的遍历过程结束后,判断图中是否还有未被访问的点,由于图 1 中没有了,所以整个图遍历结束。遍历顶点的顺序为:

V1 -> V2 -> v3 -> V4 -> V5 -> V6 -> V7 -> V8
伪代码实现:
1、初始化对列Q;visited[n]=0
2、访问顶点v;visited[v]=1;顶点v入队列Q;
3、while(队列Q非空){
        v = 队列的队首元素出队;
        w = 顶点v的第一个邻接点;
        while(w存在){
            如果w为访问,则访问顶点w;
            visited[w]=1;
            顶点w入队列Q;
            w=顶点v的下一个邻接点.
    }
}

广度优先搜索借助辅助队列实现代码:

#include <stdio.h>

typedef enum{false,true}bool;            //定义bool型常量
bool visited[20];                        //设置全局数组,记录标记顶点是否被访问过
typedef struct Queue{
    int data;
    struct Queue * next;
}Queue;
typedef struct {
    int adj;                            //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
    char * info;                        //弧或边额外含有的信息指针
}ArcCell,AdjMatrix[20][20];

typedef struct {
    int vexs[20];                         //存储图中顶点数据
    AdjMatrix arcs;                       //二维数组,记录顶点之间的关系
    int vexnum,arcnum;                    //记录图的顶点数和弧(边)数
}MGraph;

//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph * G,int v){
    int i=0;
    //遍历一维数组,找到变量v
    for (; i<G->vexnum; i++) {
        if (G->vexs[i]==v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i>G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//构造无向图
void CreateDN(MGraph *G){
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    int i ,j;
    for (i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (i=0; i<G->vexnum; i++) {
        for (j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    for (i=0; i<G->arcnum; i++) {
        int v1,v2;
        scanf("%d,%d",&v1,&v2);
        int n=LocateVex(G, v1);
        int m=LocateVex(G, v2);
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj=1;
        G->arcs[m][n].adj=1;//无向图的二阶矩阵沿主对角线对称
    }

}

int FirstAdjVex(MGraph G,int v)
{
    int i;
    //查找与数组下标为v的顶点之间有边的顶点,返回它在数组中的下标
    for(i = 0; i<G.vexnum; i++){
        if( G.arcs[v][i].adj ){
            return i;
        }
    }
    return -1;
}
int NextAdjVex(MGraph G,int v,int w)
{
    int i;
    //从前一个访问位置w的下一个位置开始,查找之间有边的顶点
    for(i = w+1; i<G.vexnum; i++){
        if(G.arcs[v][i].adj){
            return i;
        }
    }
    return -1;
}

//初始化队列
void InitQueue(Queue ** Q){
    (*Q)=(Queue*)malloc(sizeof(Queue));
    (*Q)->next=NULL;
}
//顶点元素v进队列
void EnQueue(Queue **Q,int v){
    Queue * element=(Queue*)malloc(sizeof(Queue));
    element->data=v;
    (*Q)->next = element;
    element->next = NULL;
    *Q = element;
}
//队头元素出队列
void DeQueue(Queue **Q,int *u){
    (*u)=(*Q)->next->data;
    (*Q)->next=(*Q)->next->next;

}
//判断队列是否为空
bool QueueEmpty(Queue *Q){
    if (Q->next==NULL) {
        return true;
    }
    return false;
}
//广度优先搜索
void BFSTraverse(MGraph G){//
    int v;
    //将用做标记的visit数组初始化为false
    for( v = 0; v < G.vexnum; ++v){
        visited[v] = false;
    }
    //对于每个标记为false的顶点调用深度优先搜索函数
    Queue * Q;
    InitQueue(&Q);
    for( v = 0; v < G.vexnum; v++){
        if(!visited[v]){
            visited[v]=true;
            printf("%d ",G.vexs[v]);
            EnQueue(&Q, G.vexs[v]);

            while (!QueueEmpty(Q)) {
                int u;
                DeQueue(&Q, &u);
                u=LocateVex(&G, u);

                int w;

                for (w=FirstAdjVex(G, u); w>=0; w=NextAdjVex(G, u, w)){

                    if (!visited[w]) {
                        visited[w]=true;
                        printf("%d ",G.vexs[w]);
                        EnQueue(&Q, G.vexs[w]);
                    }
                }
            }
        }
    }
}
int main() {
    MGraph G;//建立一个图的变量
    CreateDN(&G);//初始化图
    printf("广度优先遍历:");
    BFSTraverse(G);//广度优先搜索图
    return 0;
}
执行结果.png

9.3 附录(额外的版本)

我们换一种更为容易理解的方式看图的遍历,邻接矩阵深度和广度遍历的实现,利用循环队列,更节省资源消耗,更避免了反复的内存申请与释放。
邻接矩阵深度和广度遍历代码如下:

#include <stdio.h>

typedef struct
{
    int vexs[8];     //存储顶点
    int arc[8][8];    //存储边的关系
    int numVertexes;  //顶点个数
    int numEdges;     //边的个数
}MGraph;


//循环队列的顺序存储结构
typedef struct
{
    int data[8];
    int front;      //头指针
    int rear;       //尾指针,若队列不空,指向队列尾元素的下一个位置
}Queue;

//初始化一个空队列Q
int InitQueue(Queue *Q)
{
    Q->front=0;
    Q->rear=0;
    return  1;
}

//若队列Q为空队列,则返回1,否则返回0/
int QueueEmpty(Queue Q)
{
    if(Q.front==Q.rear){ //队列空的标志
        return 1;
    }
    else{
        return 0;
    }
}

//若队列未满,则插入元素e为Q新的队尾元素
int EnQueue(Queue *Q,int e)
{
    if ((Q->rear+1)%8 == Q->front)  //队列满的判断
        return 0;
    Q->data[Q->rear] = e;               //将元素e赋值给队尾
    Q->rear=(Q->rear+1)%8;          // rear指针向后移一位置,若到最后则转到数组头部
    return  1;
}

//若队列不空,则删除Q中队头元素,用e返回其值
int DeQueue(Queue *Q,int *e)
{
    if (Q->front == Q->rear){           //队列空的判断
        return 0;
    }
    *e=Q->data[Q->front];               //将队头元素赋值给e
    Q->front=(Q->front+1)%9;            //front指针向后移一位置,若到最后则转到数组头部
    return  1;
}

//创建无向图,8个顶点9个边,固定数据进行初始化,打印矩阵结构
void CreateMGraph(MGraph *G){

    int i, j;

    G->numEdges=9;
    G->numVertexes=8;

    //建立顶点表信息
    G->vexs[0]= 1;
    G->vexs[1]= 2;
    G->vexs[2]= 3;
    G->vexs[3]= 4;
    G->vexs[4]= 5;
    G->vexs[5]= 6;
    G->vexs[6]= 7;
    G->vexs[7]= 8;

    //初始化图
    for (i = 0; i < G->numVertexes; i++){
        for ( j = 0; j < G->numVertexes; j++){
            G->arc[i][j]=0;
        }
    }

    G->arc[0][1]=1;
    G->arc[1][3]=1;
    G->arc[1][4]=1;
    G->arc[3][7]=1;
    G->arc[4][7]=1;
    G->arc[0][2]=1;
    G->arc[2][5]=1;
    G->arc[2][6]=1;
    G->arc[5][6]=1;


    for(i = 0; i < G->numVertexes; i++){
        for(j = i; j < G->numVertexes; j++){
            G->arc[j][i] = G->arc[i][j];
        }
    }

    //生成矩阵形式图结构
    for(i = 0; i < G->numVertexes; i++){
        for(j = 0; j < G->numVertexes; j++){
            printf("%d ", G->arc[i][j]);
        }
        printf("\n");
    }

}

int visited[9]; // 访问标记的数组

//邻接矩阵的深度优先递归算法
void DFS(MGraph G, int i){
    int j;
    visited[i] = 1;
    printf("%d ", 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] = 0; //初始所有顶点状态都是未访问过状态
    }
    for(i = 0; i < G.numVertexes; i++){
        if(!visited[i]){ //对未访问过的顶点调用DFS,若是连通图,只会执行一次
            DFS(G, i);
        }
    }
}

/* 邻接矩阵的广度遍历算法 */
void BFSTraverse(MGraph G){

    int i, j;
    Queue Q;
    for(i = 0; i < G.numVertexes; i++){
        visited[i] = 0;
    }
    InitQueue(&Q);      //初始化一辅助用的队列
    for(i = 0; i < G.numVertexes; i++){  // 对每一个顶点做循环
        if (!visited[i]){                //若是未访问过就处理
            visited[i]=1;                //设置当前顶点访问过
            printf("%d ", 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]=1;               //将找到的此顶点标记为已访问
                        printf("%d ", G.vexs[j]);   //打印顶点
                        EnQueue(&Q,j);              //将找到的此顶点入队列
                    }
                }
            }
        }
    }
}


int main(void)
{
    MGraph G;
    CreateMGraph(&G);
    printf("\n深度遍历:");
    DFSTraverse(G);
    printf("\n广度遍历:");
    BFSTraverse(G);
    return 0;
}

image.png

利用邻接矩阵的数据初始化邻接表实现深度和广度遍历
邻接表深度和广度遍历代码如下:

#include <stdio.h>

/* ****************************************************** */
//邻接矩阵结构
typedef struct{
    char vexs[8];              // 存储顶点
    int arc[8][8];             // 邻接矩阵,可看作边表
    int numVertexes;           // 顶点数
    int numEdges;              // 边数
}MGraph;
/* ****************************************************** */

/* ****************************************************** */
//邻接表结构
typedef struct EdgeNode{       //边表结点
    int adjvex;                //邻接点数据域,存储该顶点对应的下标
    int weight;                //用于存储权值,对于非网图可以不需要
    struct EdgeNode *next;     //链域,指向下一个邻接点
}EdgeNode;

typedef struct VertexNode {    //顶点表结点
    int in;                    //顶点入度
    char data;                 //顶点域,存储顶点信息
    EdgeNode *firstedge;       //边表头指针
}VertexNode, AdjList[8];

typedef struct{
    AdjList adjList;
    int numVertexes,numEdges;  //图中当前顶点数和边数
}graphAdjList,*GraphAdjList;
/* ****************************************************** */

/* ****************************************************** */
//用到的队列结构与函数
//循环队列的顺序存储结构
typedef struct{
    int data[8];
    int front;      //头指针
    int rear;       //尾指针,若队列不空,指向队列尾元素的下一个位置
}Queue;

//初始化一个空队列Q
int InitQueue(Queue *Q){
    Q->front=0;
    Q->rear=0;
    return  1;
}

//若队列Q为空队列,则返回1,否则返回0
int QueueEmpty(Queue Q){
    if(Q.front==Q.rear){ //队列空的标志
        return 1;
    }
    else{
        return 0;
    }
}

//若队列未满,则插入元素e为Q新的队尾元素
int EnQueue(Queue *Q,int e){
    if ((Q->rear+1)%8 == Q->front)  //队列满的判断
        return 0;
    Q->data[Q->rear]=e;             // 将元素e赋值给队尾
    Q->rear=(Q->rear+1)%8;          // rear指针向后移一位置, 若到最后则转到数组头部
    return  1;
}

//若队列不空,则删除Q中队头元素,用e返回其值
int DeQueue(Queue *Q,int *e){
    if (Q->front == Q->rear){           //队列空的判断
        return 0;
    }
    *e=Q->data[Q->front];               //将队头元素赋值给e
    Q->front=(Q->front+1)%8;            //front指针向后移一位置, 若到最后则转到数组头部
    return  1;
}
/* ****************************************************** */

void CreateMGraph(MGraph *G){
    int i, j;

    G->numEdges=9;
    G->numVertexes=8;

    //建立顶点表信息
    G->vexs[0]= 1;
    G->vexs[1]= 2;
    G->vexs[2]= 3;
    G->vexs[3]= 4;
    G->vexs[4]= 5;
    G->vexs[5]= 6;
    G->vexs[6]= 7;
    G->vexs[7]= 8;

    //初始化图
    for (i = 0; i < G->numVertexes; i++){
        for ( j = 0; j < G->numVertexes; j++){
            G->arc[i][j]=0;
        }
    }

    G->arc[0][1]=1;
    G->arc[1][3]=1;
    G->arc[1][4]=1;
    G->arc[3][7]=1;
    G->arc[4][7]=1;
    G->arc[0][2]=1;
    G->arc[2][5]=1;
    G->arc[2][6]=1;
    G->arc[5][6]=1;


    for(i = 0; i < G->numVertexes; i++){
        for(j = i; j < G->numVertexes; j++){
            G->arc[j][i] =G->arc[i][j];
        }
    }

    //生成矩阵形式图结构输出
    for(i = 0; i < G->numVertexes; i++){
        for(j = 0; j < G->numVertexes; j++){
            printf("%d ", G->arc[i][j]);
        }
        printf("\n");
    }
}

//利用邻接矩阵构建邻接表
void CreateALGraph(MGraph G,GraphAdjList *GL){
    int i,j;
    EdgeNode *e;

    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));

    (*GL)->numVertexes=G.numVertexes;
    (*GL)->numEdges=G.numEdges;
    for(i= 0;i <G.numVertexes;i++){ //读入顶点信息,建立顶点表
        (*GL)->adjList[i].in=0;
        (*GL)->adjList[i].data=G.vexs[i];
        (*GL)->adjList[i].firstedge=NULL;   // 将边表置为空表
    }

    for(i=0;i<G.numVertexes;i++){ //建立边表
        for(j=0;j<G.numVertexes;j++){
            if (G.arc[i][j]==1)
            {
                e=(EdgeNode *)malloc(sizeof(EdgeNode));
                e->adjvex=j;                            //邻接序号为j
                e->next=(*GL)->adjList[i].firstedge;    //将当前顶点上的指向的结点指针赋值给e
                (*GL)->adjList[i].firstedge=e;          // 将当前顶点的指针指向e
                (*GL)->adjList[j].in++;
            }
        }
    }
}

int visited[8]; // 访问标志的数组

//邻接表的深度优先递归算法
void DFS(GraphAdjList GL, int i){
    EdgeNode *p;
    visited[i] = 1;
    printf("%d ",GL->adjList[i].data);//打印顶点,也可以其它操作
    p = GL->adjList[i].firstedge;
    while(p){
        if(!visited[p->adjvex]){
            DFS(GL, p->adjvex);//对为访问的邻接顶点递归调用
        }
        p = p->next;
    }
}

//邻接表的深度遍历操作
void DFSTraverse(GraphAdjList GL)
{
    int i;
    for(i = 0; i < GL->numVertexes; i++){
        visited[i] = 0; // 初始所有顶点状态都是未访问过状态
    }
    for(i = 0; i < GL->numVertexes; i++){
        if(!visited[i]){ //对未访问过的顶点调用DFS,若是连通图,只会执行一次
            DFS(GL, i);
        }
    }
}

//邻接表的广度遍历算法
void BFSTraverse(GraphAdjList GL)
{
    int i;
    EdgeNode *p;
    Queue Q;
    for(i = 0; i < GL->numVertexes; i++){
        visited[i] = 0;
    }
    InitQueue(&Q);
    for(i = 0; i < GL->numVertexes; i++){
        if (!visited[i]){
            visited[i]=1;
            printf("%d ",GL->adjList[i].data);//打印顶点
            EnQueue(&Q,i);
            while(!QueueEmpty(Q)){
                DeQueue(&Q,&i);
                p = GL->adjList[i].firstedge;   //找到当前顶点的边表链表头指针
                while(p){
                    if(!visited[p->adjvex]){    //若此顶点未被访问
                        visited[p->adjvex]=1;
                        printf("%d ",GL->adjList[p->adjvex].data);
                        EnQueue(&Q,p->adjvex);  //将此顶点入队列
                    }
                    p = p->next;    //指针指向下一个邻接点
                }
            }
        }
    }
}

int main(void){
    MGraph G;
    GraphAdjList GL;
    CreateMGraph(&G);
    CreateALGraph(G,&GL);

    printf("\n深度遍历:");
    DFSTraverse(GL);
    printf("\n广度遍历:");
    BFSTraverse(GL);
    return 0;
}

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335