PTA | 6-2 邻接表存储图的广度优先遍历

试实现邻接表存储图的广度优先遍历。

函数接口定义:

 

void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) );

其中LGraph是邻接表存储的图,定义如下:

/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode; 
struct AdjVNode{
    Vertex AdjV;        /* 邻接点下标 */
    PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};

/* 顶点表头结点的定义 */
typedef struct Vnode{
    PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum];     /* AdjList是邻接表类型 */

/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{  
    int Nv;     /* 顶点数 */
    int Ne;     /* 边数   */
    AdjList G;  /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */

函数BFS应从第S个顶点出发对邻接表存储的图Graph进行广度优先搜索,遍历时用裁判定义的函数Visit访问每个顶点。当访问邻接点时,要求按邻接表顺序访问。题目保证S是图中的合法顶点。

裁判测试程序样例:

#include <stdio.h>

typedef enum {false, true} bool;
#define MaxVertexNum 10   /* 最大顶点数设为10 */
typedef int Vertex;       /* 用顶点下标表示顶点,为整型 */

/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode; 
struct AdjVNode{
    Vertex AdjV;        /* 邻接点下标 */
    PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};

/* 顶点表头结点的定义 */
typedef struct Vnode{
    PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum];     /* AdjList是邻接表类型 */

/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{  
    int Nv;     /* 顶点数 */
    int Ne;     /* 边数   */
    AdjList G;  /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */

bool Visited[MaxVertexNum]; /* 顶点的访问标记 */

LGraph CreateGraph(); /* 创建图并且将Visited初始化为false;裁判实现,细节不表 */

void Visit( Vertex V )
{
    printf(" %d", V);
}

void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) );

int main()
{
    LGraph G;
    Vertex S;

    G = CreateGraph();
    scanf("%d", &S);
    printf("BFS from %d:", S);
    BFS(G, S, Visit);

    return 0;
}

/* 你的代码将被嵌在这里 */

输入样例:给定图如下

2

输出样例:

BFS from 2: 2 0 3 5 4 1 6

代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB

分析:

图的广度遍历类似于树的层序遍历,都采用队列进行辅助遍历。首先遍历自己以及后继节点,加入到队列中,直到队列为空。

代码:

void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) ){

    Visit(S);
    Visited[S]=true;

    int a[1000]={0};
    int end=0;
    int begin=0;

    a[end++]=S;
    while(begin<end){

        PtrToAdjVNode cur = Graph->G[a[begin++]].FirstEdge;
        while(cur){
            if(!Visited[cur->AdjV]){
                Visit(cur->AdjV);
                Visited[cur->AdjV]= true;
                a[end++]=cur->AdjV;
            }
            cur=cur->Next;
        }
    }
    
}

注意点:

1.采用数组模拟队列。

2.为什么a[end++]=cur->AdjV放在!Visited[cur->Next]条件内:

        如果节点已经被访问过,则代表其以及它的后继节点都已经加入到队列中,无需再重复遍历。

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 对于关系表存储广度优先遍历,可以按照以下步骤进行: 1. 定义一个队列, 将初始节点入队 2. 取队首节点并遍历它的所有邻居节点 3. 若邻居节点未被访问, 则加入队列并标记为已访问 4. 重复步骤2和3, 直到队列为空 通过广度优先遍历可以得到关系表存储中所有节点的广度优先遍历序列, 从而实现对的遍历。 ### 回答2: 邻接表是一种存储方式,广度优先遍历是一种搜索算法,对于一个新手程序员来说,熟练掌握邻接表存储广度优先遍历是非常重要的。 邻接表是一种基于链表的存储方式,它把每个节点及其邻接的节点连成一个链表。对于无向来说,每个节点的邻接节点都要加上相应的边,而对于有向来说,边就只能从节点指向其它某个节点。 广度优先遍历(BFS)是一种搜索算法,它从起点开始遍历,先访问起点直接相连的所有点,再访问这些点直接相连的所有点,以此类推,直到找到终点或者遍历完的所有节点。 实现广度优先遍历的基本操作是使用队列(Queue),从起点开始,将其放置在队列中,每次从队列中取出一个节点,遍历其相邻节点并访问并把其加入队列中。访问的过程需要标记节点是否已经被访问过,避免重复访问。 下面我们来模拟一下广度优先遍历过程: 1. 先把起点节点加入队列中,同时标记其已经被访问过。 2. 从队列中取出起点节点,并遍历其相邻节点。 3. 如果相邻节点没有被遍历过,就加入队列中,并标记它已经被遍历过。 4. 重复步骤2、3,直到队列为空。 使用邻接表存储,可以实现广度优先遍历。具体的实现思路是,用一个布尔型数组visited(访问标记数组)来记录每个节点是否被遍历过,再用一个队列Queue来存储节点。遍历的过程如下: 1. 先将起点节点加入队列中,并标记为已经访问过。 2. 从队列中取出一个节点进行访问,并遍历其所有相邻节点。 3. 如果相邻节点没有被访问过,就将其加入队列中,并标记为已经访问过。 4. 重复步骤2、3,直到队列为空。 值得一提的是,广度优先遍历的时间复杂度为O(N),其中N为节点的个数。因为每个节点最多被遍历一次,而每次遍历时只需要考虑该节点相邻的节点。因此,在某些情况下,采用广度优先遍历可以有效提高算法效率。 ### 回答3: 广度优先遍历是一种用来访问和搜索形数据结构的算法,它从起始顶点开始遍历整个形,逐层向外遍历,直到将整个形访问完毕。广度优先遍历的核心思想是先访问所有与起始顶点相邻的顶点,然后再依次访问这些顶点的邻居顶点,直到遍历完整个形。 邻接表是一种常用的存储形数据结构的方式,它使用链表来表示每个顶点的邻居顶点。邻接表存储广度优先遍历的基本步骤如下: 1. 创建一个队列,将起始顶点存入队列中; 2. 标记起始顶点为已访问; 3. 从队列中取出一个顶点,访问它的所有邻居顶点,将它们存入队列中,并标记它们为已访问; 4. 重复步骤3,直到队列为空。 在代码实现上,可以使用一个数组来记录每个顶点是否已经被访问过,以免重复访问。同时,在访问每个顶点时,还可以记录其前驱节点,以方便后续的路径查找。 对于一个有n个顶点和m条边的无向邻接表存储广度优先遍历的时间复杂度为O(n+m),空间复杂度为O(n+m)。在实际应用中,广度优先遍历常用于网络路由、社交网络分析和机器人路径规划等领域中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值