#头歌 数据结构 实验九 图的邻接表存储及操作

第1关:实现图的深度优先遍历

任务描述

本关任务:实现 graph.cpp 里的函数void DFS(AdjGraph *G,int v),实现图的深度优先遍历。 注意遵守约定:始终从编号小的开始遍历。

相关知识

从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。如果给定图是连通的无向图或者是强连通的有向图,则遍历过程一次就能完成,并可按访问的先后顺序得到由该图的所有顶点组成的一个序列。 深度优先遍历的过程是从图中的某个初始点 v 出发,首先访问初始点 v 然后选择一个与顶点 v 相邻且没被访问过的顶点 w 为初始顶点,再从它出发进行深度优先遍历,直到图中与顶点 v 邻接的所有顶点都被访问过为止,显然这个遍历过程是一个递归过程。 例如,对于图 1 所示的有向图,从顶点 0 开始进行深度优先遍历,可以得到以下访问序列:01243, 03241。


图1

下面以图 1 为例调用 DFS(G,0) 算法,其对应的邻接表如下:


图2

(1). DFS(G,0):访问顶点 0,找到顶点 0 相邻的顶点 1 , 它未被访问过,转 (2); (2). DFS(G,1):访问顶点 1,找到顶点 1 相邻的顶点 2 , 它未被访问过,转 (3); (3). DFS(G,2):访问顶点 2,找到顶点 2 相邻的顶点 4 , 它未被访问过,转 (4); (4). DFS(G,4):访问顶点 4,找到顶点 3 相邻的顶点 4 , 它未被访问过,转 (4);
(5). 继续 DFS(G,4):其后继结点相邻结点均已被访问,退出 DFS(G,4) ,转 (6) ; (6). 继续 DFS(G,2):其后继结点相邻结点均已被访问,退出 DFS(G,2) ,转 (7) ; (7). 继续 DFS(G,1):访问顶点 3,其后继结点均被访问,退出 DFS(G,1),转 (8) ; (8). 结束。

图采用邻接表的方式存储,结构体如下:

 
  1. #define MAXV 50
  2. #define INF 32767
  3. typedef struct ANode
  4. {
  5. int adjvex; //该边的邻接点编号
  6. struct ANode *nextarc; //指向下一条边的指针
  7. int weight; //该边的相关信息,如权值(这里用整型表示)
  8. }ArcNode; //边结点类型
  9. typedef struct Vnode
  10. {
  11. //InfoType info //顶点的其他信息
  12. ArcNode *firstarc; //指向第一个边结点
  13. }VNode;
  14. typedef struct
  15. {
  16. VNode adjlist[MAXV]; //邻接表的头结点数组
  17. int n,e; //图中的顶点数n和边数e
  18. }AdjGraph ; //完整的图邻接表类型
编程要求

本关任务是实现 graph.cpp 里的函数void DFS(AdjGraph *G,int v)以完成邻接表为存储结构的深度优先遍历算法,其中 v 是初始点 visited 是一个全局数组,初始时所有元素均为 0 , 表示所有顶点尚未被访问过。 注意遵守约定:始终从编号小的开始遍历。 测试文件如下: main.cpp:

 
  1. #include "graph.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. int main()
  5. {
  6. int i,j,n,e;
  7. int A[MAXV][MAXV];
  8. scanf("%d %d",&n,&e);
  9. AdjGraph *G;
  10. for(i=0;i<n;i++)
  11. {
  12. for(j=0;j<n;j++)
  13. {
  14. scanf("%d",&A[i][j]);
  15. }
  16. }
  17. CreateAdj(G,A,n,e);
  18. DFS(G,0);
  19. printf("\n");
  20. DestroyAdj(G);
  21. return 0;
  22. }
测试说明

本关的测试过程如下:

  1. 平台编译 step3/main.cpp ;
  2. 平台运行该可执行文件,并以标准输入方式提供测试输入;
  3. 平台获取该可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。

输入格式: 输入n e,顶点数 边数 输入一个n×n的一个矩阵表示一个图的邻接矩阵,在带权有向图中,用 32767 代替无穷大。

输出格式: 以顶点开始的深度优先遍历序列。

以下是平台对 step3/main.cpp 的测试样例: 样例输入

 
  1. 5 5
  2. 0 8 32767 5 32767
  3. 32767 0 3 32767 32767
  4. 32767 32767 0 32767 6
  5. 32767 32767 9 0 32767
  6. 32767 32767 32767 32767 0

样例输出 0 1 2 4 3

参考资料

数据结构(李春葆版)


开始你的任务吧,祝你成功!

最后通关代码

#include "graph.h"

#include <stdio.h>

#include <stdlib.h>

void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e)  //创建图的邻接表

{

    int i,j;

    ArcNode *p;

    G=(AdjGraph *)malloc(sizeof(AdjGraph));

    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(A[i][j]!=0&&A[i][j]!=INF)                    //存在一条边

            {

                p=(ArcNode *)malloc(sizeof(ArcNode));       //创建一个结点p

                p->adjvex=j;                                //存放邻接点

                p->weight=A[i][j];                          //存放权

                p->nextarc=G->adjlist[i].firstarc;          //采用头插法插入结点p

                G->adjlist[i].firstarc=p;

            }

        }

    }

    G->n=n;

    G->e=e;

}

void DispAdj(AdjGraph *G)

{

    int i;

    ArcNode *p;

    for(i=0;i<G->n;i++)

    {

        p=G->adjlist[i].firstarc;

        printf("%3d: ",i);

        while(p!=NULL)

        {

            printf("%d[%d]→",p->adjvex,p->weight);

            p=p->nextarc;

        }

        printf("^\n");

    }

}

void DestroyAdj(AdjGraph *&G)

{

    int i;

    ArcNode *pre,*p;

    for(i=0;i<G->n;i++)                 //扫描所有单链表

    {

        pre=G->adjlist[i].firstarc;     //p指向第i个单链表的头结点

        if(pre!=NULL)

        {

            p=pre->nextarc;

            while(p!=NULL)              //释放第i个单链表的所有结点

            {

                free(pre);

                pre=p;

                p=p->nextarc;

            }

            free(pre);

        }

    }

    free(G);

}

//深度优先遍历

int visited[MAXV];                //全局数组,记录访问的点

void DFS(AdjGraph *G,int v)

{

    /********** Begin **********/

    visited[v] = 1;

    printf("%d  ", v);

    ANode *p = G -> adjlist[v].firstarc;

    while(p){

        if(!visited[p -> adjvex]){

            DFS(G, p -> adjvex);

            p = p -> nextarc;

        }

        else{

            p = p -> nextarc;

            continue;

        }

    }

    /********** End **********/

}

第2关:实现图的广度优先遍历

任务描述

本关任务:实现 graph.cpp 里的函数void BFS(AdjGraph *G,int v);,实现图的广度优先遍历。 注意遵守约定:始终从编号小的开始遍历。

相关知识

广度优先遍历的过程是首先访问初始点 v ,接着访问顶点 v 的所有未被访问过的邻接 v1​,v2​,•••,vt​ , 然后再按照 v1​,v2​,•••,vt​ 的次序访问每个顶点的所有未被访问过的邻接点,依此类推,直到图中所有和初始点 v 有路径相通的顶点都被访问过为止。 例如,对于图 1 所示的有向图, 从顶点 0 开始进行广度优先遍历,可得到以下访问序列 0 1 3 2 4 , 0 3 1 2 4。

以邻接表为存储结构,在用广度优先遍历图时需要使用一个队列,这里采用环形队列,以类似于 叉树的层次遍历方式来遍历图。


图1

下面以图 1 为例调用 BFS(G,0) 算法,其对应的邻接表如下:


图2

(1). 访问顶点 0 , 0 进队,转 (2) ; (2). 第 1 次循环:顶点 0出队,找其第一个相邻的顶点 1 ,它未被访问过,访问之并将 1 进队;找顶点 0 的下一个邻接点 3 ,它未被访问,访问之并将 3 进队,转 (3); (3). 第 2 次循环:顶点 1 出队,找其第一个相邻的结点 2 ,它未被访问过,访问之并将 2 进队,转 (4); (4). 顶点 3 出队,找 2 ,已被访问过,转 (5);
(5). 顶点 2 出队,找顶点 4 ,它未被访问,访问之,并将 4 进队 转 (6) ; (6). 顶点 4 出队, 无相邻结点,转 (7); (7). 队列为空,遍历结束,即 0 1 3 2 4。

图采用邻接表的方式存储,结构体如下:

 
  1. #define MAXV 50
  2. #define INF 32767
  3. typedef struct ANode
  4. {
  5. int adjvex; //该边的邻接点编号
  6. struct ANode *nextarc; //指向下一条边的指针
  7. int weight; //该边的相关信息,如权值(这里用整型表示)
  8. }ArcNode; //边结点类型
  9. typedef struct Vnode
  10. {
  11. //InfoType info //顶点的其他信息
  12. ArcNode *firstarc; //指向第一个边结点
  13. }VNode;
  14. typedef struct
  15. {
  16. VNode adjlist[MAXV]; //邻接表的头结点数组
  17. int n,e; //图中的顶点数n和边数e
  18. }AdjGraph ; //完整的图邻接表类型
编程要求

本关任务是实现 graph.cpp 里的函数void BFS(AdjGraph *G,int v);以完成邻接表为存储结构的广度优先遍历算法,其中 v 是初始点。 注意遵守约定:始终从编号小的开始遍历。 测试文件如下: main.cpp:

 
  1. #include "graph.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. int main()
  5. {
  6. int i,j,n,e;
  7. int A[MAXV][MAXV];
  8. scanf("%d %d",&n,&e);
  9. AdjGraph *G;
  10. for(i=0;i<n;i++)
  11. {
  12. for(j=0;j<n;j++)
  13. {
  14. scanf("%d",&A[i][j]);
  15. }
  16. }
  17. CreateAdj(G,A,n,e);
  18. BFS(G,0);
  19. printf("\n");
  20. DestroyAdj(G);
  21. return 0;
  22. }
测试说明

本关的测试过程如下:

  1. 平台编译 step4/main.cpp ;
  2. 平台运行该可执行文件,并以标准输入方式提供测试输入;
  3. 平台获取该可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。

输入格式: 输入n e,顶点数 边数 输入一个n×n的一个矩阵表示一个图的邻接矩阵,在带权有向图中,用 32767 代替无穷大。

输出格式: 以顶点开始的广度优先遍历序列。

以下是平台对 step3/main.cpp 的测试样例: 样例输入

 
  1. 5 5
  2. 0 8 32767 5 32767
  3. 32767 0 3 32767 32767
  4. 32767 32767 0 32767 6
  5. 32767 32767 9 0 32767
  6. 32767 32767 32767 32767 0

样例输出 0 1 3 2 4

参考资料

数据结构(李春葆版)


开始你的任务吧,祝你成功!

最后通关代码

#include "graph.h"

#include <stdio.h>

#include <stdlib.h>

void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e)  //创建图的邻接表

{

    int i,j;

    ArcNode *p;

    G=(AdjGraph *)malloc(sizeof(AdjGraph));

    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(A[i][j]!=0&&A[i][j]!=INF)                    //存在一条边

            {

                p=(ArcNode *)malloc(sizeof(ArcNode));       //创建一个结点p

                p->adjvex=j;                                //存放邻接点

                p->weight=A[i][j];                          //存放权

                p->nextarc=G->adjlist[i].firstarc;          //采用头插法插入结点p

                G->adjlist[i].firstarc=p;

            }

        }

    }

    G->n=n;

    G->e=e;

}

void DispAdj(AdjGraph *G)

{

    int i;

    ArcNode *p;

    for(i=0;i<G->n;i++)

    {

        p=G->adjlist[i].firstarc;

        printf("%3d: ",i);

        while(p!=NULL)

        {

            printf("%d[%d]→",p->adjvex,p->weight);

            p=p->nextarc;

        }

        printf("^\n");

    }

}

void DestroyAdj(AdjGraph *&G)

{

    int i;

    ArcNode *pre,*p;

    for(i=0;i<G->n;i++)                 //扫描所有单链表

    {

        pre=G->adjlist[i].firstarc;     //p指向第i个单链表的头结点

        if(pre!=NULL)

        {

            p=pre->nextarc;

            while(p!=NULL)              //释放第i个单链表的所有结点

            {

                free(pre);

                pre=p;

                p=p->nextarc;

            }

            free(pre);

        }

    }

    free(G);

}

//广度优先遍历

void BFS(AdjGraph *G,int v)

{

    /********** Begin **********/

    ArcNode *p;

    int w,i;

    int queue[MAXV],front=0,rear=0;

    int visited[MAXV];

    for(i=0;i<G->n;i++) visited[i]=0;

    printf("%2d",v);

    visited[v]=1;

    rear=(rear+1)%MAXV;

    queue[rear]=v;

    while(front!=rear){

        front=(front+1)%MAXV;

        w=queue[front];

        p=G->adjlist[w].firstarc;

        while(p!=NULL)

        {

            if(visited[p->adjvex]==0)

            {

                printf("%2d",p->adjvex);

                visited[p->adjvex]=1;

                rear=(rear+1)%MAXV;

                queue[rear]=p->adjvex;

            }

            p=p->nextarc;

        }

    }

    printf("\n");

    /********** End **********/

}

第3关:邻接矩阵转邻接表

本关任务要求实现ALGraph ChangeToTable(NMGraph &graph,ALGraph &graph2)函数,来完成邻接矩阵到邻接表的转换。

最后通关代码:

#include<stdio.h>

#include<stdlib.h>

#define MaxVertexNum 100

#define ElemType int

#define VertexType int

typedef struct{//邻接矩阵存储

    ElemType Vertices[MaxVertexNum];  //顶点信息的数组

    ElemType Edge[MaxVertexNum][MaxVertexNum]; //边信息的数组

    ElemType vexnum; //顶点数

    ElemType arcnum;//边数

}NMGraph;

typedef struct ArcNode{//边表结点

    int adjvex;

    struct ArcNode *next;

    //InfoType info; //网的边权值

}ArcNode;

typedef struct VNode{//顶点表结点

    VertexType data; //顶点信息

    ArcNode *first;

}VMode,AdjList[MaxVertexNum];

typedef struct{

    AdjList vertices; //邻接表

    int vexnum,arcnum;;  //顶点数和边数

}ALGraph;

void CreateGraph(int vnum,int anum,NMGraph &graph,ALGraph &graph2){

    //邻接矩阵

    graph.vexnum=vnum;

    graph.arcnum=anum;

    //邻接表

    graph2.vexnum=vnum;

    graph2.arcnum=anum;

    for(int i=0;i<vnum;i++){//图的初始化

        for(int j=0;j<vnum;j++){

            graph.Edge[i][j]=0;

        }

    }//for

    for(int i=0;i<vnum;i++){//存储顶点信息

        int value;

        scanf("%d",&value);

        //邻接矩阵

        graph.Vertices[i]=value;

        //邻接表

        graph2.vertices[i].data=value;

        graph2.vertices[i].first=NULL;

    }

    printf("\n");

    for(int i=0;i<anum;i++){//存储边表信息

        int v1,v2,w;

        scanf("%d%d%d",&v1,&v2,&w);

        //无向图

        graph.Edge[v1-1][v2-1]=w;

        graph.Edge[v2-1][v1-1]=w;

    }

}

ALGraph ChangeToTable(NMGraph &graph,ALGraph &graph2){

    /********** Begin **********/

      for(int i=0;i<graph2.vexnum;i++){

        ArcNode *head=(ArcNode*)malloc(sizeof(ArcNode));

         ArcNode *p=head;

        p->next=NULL;

        for(int j=0;j<graph.vexnum;j++){

            if(graph.Edge[i][j] !=0){

                ArcNode *edgeNode=(ArcNode*)malloc(sizeof(ArcNode));

                edgeNode->adjvex=(j+1);

                edgeNode->next=NULL;

                p->next=edgeNode;

                p=p->next;

            }

        }

        graph2.vertices[i].first=head->next;

        head->next=NULL;

    }

    /********** End **********/

}

//打印相邻结点的方法

void display(ALGraph &graph){

    for(int i=0;i<(graph.vexnum);i++){//顶点

        ArcNode *p=graph.vertices[i].first;

        printf("与顶点%d相邻的顶点为:",graph.vertices[i]);

        while(p){

            printf("%d ",p->adjvex);

            p=p->next;

        }

        printf("\n");

    }

}

//打印邻接矩阵的方法

void display2(NMGraph &graph){

    printf("  ");

    for(int i=0;i<(graph.vexnum);i++){

        printf("%d ",graph.Vertices[i]);

    }

    printf("\n");

    for(int i=0;i<(graph.vexnum);i++){//顶点

       printf("%d ",graph.Vertices[i]);

        for(int j=0;j<(graph.vexnum);j++){//边

            printf("%d ",graph.Edge[i][j]);

        }

        printf("\n");

    }

    printf("\n");

}

int main(){

    int vnum,anum;

    scanf("%d%d",&vnum,&anum);

    NMGraph graph;

    ALGraph graph2;

    CreateGraph(vnum,anum,graph,graph2);

    printf("NMGraph邻接矩阵为\n");

    display2(graph);

    printf("\n邻接矩阵转邻接表\n");

    ChangeToTable(graph,graph2);

    printf("\n邻边表为\n");

    display(graph2);

    return 0;

}

  • 21
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值