C++学习:【PTA】数据结构 7-2 实验6-2(图-邻接表)

7-2 实验6-2(图-邻接表)

利用邻接表存储无向图,并从0号顶点开始进行广度优先遍历。

输入格式:

输入第一行是两个整数n1 n2,其中n1表示顶点数(则顶点编号为0至n1-1),n2表示图中的边数。

之后有n2行输入,每行输入表示一条边,格式是"顶点1 顶点2",把边插入图中。在链表中插入元素时,在链表头部插入。需要注意,由于是无向图,因此同一条边需要在两条链表中都插入。

例如:

cpp 复制代码
4 4
0 1
1 3
0 3
0 2

输出格式:

先按0至n1-1输出存储图的邻接表,每个链表占1行,同一行元素之间空1格,最后一个元素之后不要有空格。先输出顶点编号,之后按链表顺序输出相邻顶点编号。

之后空一行后输出从0号顶点开始的广度优先遍历序列,顶点编号之间空1格。

例如,对于上面的示例输入,输出为:

cpp 复制代码
0 2 3 1
1 3 0
2 0
3 0 1

0 2 3 1

说明:输入第1个4表示有4个顶点,第2个4表示有4条边。之后的4行输入代表4条边的顶点。输出前4行为邻接表中的4个链表,之后空一行,然后输出的"0 2 3 1"是广度优先遍历序列。

输入样例:

在这里给出一组输入。例如:

cpp 复制代码
5 4
0 2
1 2
0 3
0 4

输出样例:

在这里给出相应的输出。例如:

cpp 复制代码
0 4 3 2
1 2
2 1 0
3 0
4 0

0 4 3 2 1 

代码长度限制 16 KB

时间限制 400 ms

内存限制 64 MB

栈限制 8192 KB

代码均自学参考数据结构一书完成,总结一下有两个注意点:

  1. 一是BFS遍历问题
    注意BFS需要遍历完图中所有点,如果是非连通图,很容易漏掉。
  2. 二是格式
    特别要注意格式,如果说一直提示格式错误,一定要仔细检查输出函数空格问题,我就是检查了很久才找到,当图中某个点没有邻接点时,要注意打印顶点之后,不能再打印空格。

C++代码

cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;

#define MaxVertexNum 100  // 设置最大顶点数
typedef  int DataType;  // 顶点存储的数据设为整型
typedef  int WeightType;  // 边的权重设置为整型
typedef int Vertex;  // 用定点下标设置为顶点,为整型
bool Visited[MaxVertexNum] = {false};

// 边的定义
typedef struct ENode* PtrToENode;
struct ENode {
    Vertex V1, V2;  // 有向边<V1, V2>
    WeightType Weight;  // 权重
};
typedef PtrToENode Edge;

// --------------------------组成图--------------------------
// 邻接点定义
typedef struct AdjVNode* PtrToAdjVNode;
struct AdjVNode {
    Vertex AdjV;  // 邻接点下标
    WeightType Weight;  // 边权重
    PtrToAdjVNode Next;  // 指向下一个邻接点的指针
};

// 顶点表头结点的定义
typedef struct VNode {
    PtrToAdjVNode FirstEdge;  // 边表头指针
    DataType Data;  // 存顶点的数据
} AdjList[MaxVertexNum];  // AdjList是邻接表类型,AdjList是结构体数组,长度为MaxVertexNum,元素为VNode

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

// 初始化一个有VerTexNum个顶点但没有边的图
LGraph CreatGraph(int VertexNum) {
    LGraph Graph = (LGraph)malloc(sizeof(struct GNode));
    Graph->Nv = VertexNum;
    Graph->Ne = 0;

    // 初始化邻接表头指针
    for (Vertex V = 0; V < Graph->Nv; V++) {
        Graph->G[V].FirstEdge = NULL;
    }
    return Graph;
}

void InsertEdge(LGraph Graph, Edge E) {
    // 插入边 E(<V1, V2>, W)
    // 为V1建立邻接点V2
    PtrToAdjVNode NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V2;
    NewNode->Weight = E->Weight;

    // 将V2插入图Graph
    NewNode->Next = Graph->G[E->V1].FirstEdge;  // 邻接点V2尾部指向顶点域V1所指的邻接点
    Graph->G[E->V1].FirstEdge = NewNode;  // 顶点域V1指向邻接点V2

    // 对于无向图,
    // 还要插入边 E(<V2, V1>, W)
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V1;
    NewNode->Weight = E->Weight;

    // 将V1插入图Graph
    NewNode->Next = Graph->G[E->V2].FirstEdge;  // 邻接点V1尾部指向顶点域V2所指的邻接点
    Graph->G[E->V2].FirstEdge = NewNode;  // 顶点域V2指向邻接点V1
}


// 建立以邻接表方式存储的图
LGraph BuildGraph() {

    int Nv;
    Edge E;
    LGraph Graph;


    cin >> Nv;  // 读入顶点个数

    Graph = CreatGraph(Nv);  // 初始化只有Nv个顶点的图

    if (Nv == 0) {
        Graph->Ne = 0;
        return Graph;
    }

    cin >> Graph->Ne;  // 读入边数
    if (Graph->Ne != 0) {  // 如果有边
        E = (Edge)malloc(sizeof(ENode));  // 建立边结点
        //读入边,输入格式为:起点 终点 权重
        for (int i = 0; i < Graph->Ne; i++) {
            cin >> E->V1 >> E->V2;
            E->Weight = 1;
            InsertEdge(Graph, E);  // 插入边
        }

        // 若需要输入顶点数据,写在这
    }
    return Graph;
}

// 打印图的邻接表
void PrintGraph(LGraph Graph) {
    Vertex Vi;
    PtrToAdjVNode P;

    for (Vi = 0; Vi < Graph->Nv; Vi++) {
        cout << Vi;  // 打印顶点
        if (Graph->G[Vi].FirstEdge != NULL) cout << ' ';  // 卡了很久才知道是这里的问题,要注意,如果没有邻接点,顶点后面不能输出空格,要区分好
        for (P = Graph->G[Vi].FirstEdge; P != NULL; P = P->Next) {  // 遍历Vi的邻接点
            cout << P->AdjV;  // 依次打印邻接点下标
            if(P->Next) cout << ' ';
        }
        cout << '\n';
    }
}

// BFS主函数
void BFSFormVertex(LGraph G, Vertex S, void(*Visit)(Vertex)) {
    // 以S为出发点的以邻接表方式存储的图进行BFS搜索
    Vertex V;
    PtrToAdjVNode P;
    queue<Vertex> Q;  // 创建空队列
    
    Visit(S);  // 访问S
    Visited[S] = true;  // 标记已访问
    Q.push(S);  // S入队

    while (!Q.empty()) {
        V = Q.front();Q.pop();   // 弹出队首元素V
        // 遍历V的每个邻接点,若没有访问过,则访问并入队
        for (P = G->G[V].FirstEdge; P != NULL; P = P->Next) {
            if (!Visited[P->AdjV]) {  // 如果没访问过
                Visit(P->AdjV);  // 访问
                Visited[P->AdjV] = true;  // 访问后标记
                Q.push(P->AdjV);  // 入队
            }
        }
    }
}

// 广度优先遍历BFS
void BFS(LGraph G, Vertex S, void(*Visit)(Vertex)) {
    Vertex V;
    for (V = 0; V < G->Nv; V++) {
        if (!Visited[V]) {
            // 用于访问非连通的顶点
            BFSFormVertex(G, V, Visit);
        }
    }
}

// 访问方法
void Visit(Vertex V) {
    cout << V << ' ';
}

int main(int argc, char *argv[]) {
    LGraph Graph;

    Graph = BuildGraph();  // 读入数据,建立图

    PrintGraph(Graph);  // 打印图的邻接表

    cout << '\n';  // 注意换行

    BFS(Graph, 0, Visit);  // BFS遍历图
}
相关推荐
方便面不加香菜15 小时前
基于顺序表实现通讯录项目
c语言·数据结构
好奇龙猫15 小时前
【日语学习-日语知识点小记-日本語体系構造-JLPT-N2前期阶段-第一阶段(1):再次起航】
学习
wdfk_prog16 小时前
[Linux]学习笔记系列 -- [fs]sysfs
linux·笔记·学习
꧁Q༒ོγ꧂16 小时前
算法详解(三)--递归与分治
开发语言·c++·算法·排序算法
菜的不敢吱声16 小时前
swift学习第5天
学习·ssh·swift
李兴球16 小时前
这个来自五线城市的C++兴趣班的程序可不一般
c++
MQLYES16 小时前
03-BTC-数据结构
数据结构·算法·哈希算法
White_Can16 小时前
《C++11:智能指针》
c++·c++11·智能指针
无限进步_16 小时前
【数据结构&C语言】对称二叉树的递归之美:镜像世界的探索
c语言·开发语言·数据结构·c++·算法·github·visual studio