C++学习:【PTA】数据结构 7-1 实验7-1(最小生成树-Prim算法)

7-1 实验7-1(最小生成树-Prim算法)

利用Prim算法计算最小生成树。

输入格式:

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

之后有n2行输入,每行输入表示一条边,格式是"顶点1 顶点2 权重"。其中,权重是整数类型。

例如:

bash 复制代码
4 6
0 1 4
0 2 5
0 3 6
1 2 2
1 3 1
2 3 4

输出格式:

以0号顶点为初始顶点,依次输出每轮选择的边,每条边占一行,格式为"顶点1 顶点2"。其中顶点1与0号顶点在同一个集合中。

全部边输出后再输出最小生成树的权重,之后输出一个换行符。

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

bash 复制代码
0 1
1 3
1 2
7

输入样例:

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

bash 复制代码
3 3
0 1 10
0 2 30
1 2 25

输出样例:

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

bash 复制代码
0 1
1 2
35

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

C++代码

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

#define MaxVertexNum 100  // 设置最大顶点数
#define INFINITY 65535  // 表示无穷大(无边)♾️设置为双字节无符号整型最大值65535
#define ERROR -1  // 表示无父节点
typedef  int DataType;  // 顶点存储的数据设为整型
typedef  int WeightType;  // 边的权重设置为整型
typedef int Vertex;  // 用定点下标设置为顶点,为整型

// 边的定义
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;  // 指向下一个邻接点的指针w
};

// 顶点表头结点的定义
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;
            InsertEdge(Graph, E);  // 插入边
        }

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

/*
 * 返回未收录顶点中Dist最小的顶点
 * @param Graph 邻接表图
 * @param Dist 距离数组(Dist[V]表示V到生成树的最小权值)
 * @return 最小距离的顶点下标,无则返回ERROR
 */
Vertex FindMinDist(LGraph Graph, WeightType Dist[]) {
    WeightType MinDist = INFINITY;  // 初始化最短距离
    Vertex MinV = ERROR;  // 返回未收录顶点中Dist最小的顶点

    for (Vertex V = 0; V < Graph->Nv; V++) {
        // 未收录的(Dist[V]!=0)且距离更小
        if (Dist[V] != 0 && Dist[V] < MinDist) {
            MinDist = Dist[V];
            MinV = V;
        }
    }
    return MinV;
}

// prim算法
WeightType Prim(LGraph Graph, LGraph *MST) {
    WeightType Dist[Graph->Nv];  // 距离数组
    Vertex Parent[Graph->Nv];  // 父节点数组
    WeightType totalWeight = 0;  // 最小生成树总权值
    int VCount = 0;  // 已收录的顶点数
    Vertex Start = 0;  // 起始顶点,默认选0

    // 初始化Dist和Parent数组
    for (Vertex V = 0; V < Graph->Nv; V++) {
        Dist[V] = INFINITY;
        Parent[V] = ERROR;  // ERROR表示无父结点
    }

    // 收录起始顶点Start
    Dist[Start] = 0;  // 标记起始结点Start已收录
    VCount++;  // 更新已收录顶点数

    // 初始化起始顶点的邻接边距离
    PtrToAdjVNode W = Graph->G[Start].FirstEdge;  // W为Strat的邻接点
    while (W != NULL) {  // 遍历Start的邻接点
        if (W->Weight < Dist[W->AdjV]) {
            Dist[W->AdjV] = W->Weight;  // 以Start为中心,更新Dist
            Parent[W->AdjV] = Start;  // 更新父节点
        }
        W = W->Next;  // 下一个邻接点
    }

    // 创建空的最小生成树(邻接表法)
    *MST = CreatGraph(Graph->Nv);

    // 迭代选择最小权值边,构建生成树
    for (int i = 1; i < Graph->Nv; i++) {  // 迭代选择最小权值边,构建生成树
        Vertex V = FindMinDist(Graph, Dist);  // 返回未收录顶点中Dist最小的顶点
        if (V == ERROR) break;  // 无收录顶点,退出(FindMinDist错误则返回ERROR)
        // 找到了FindMinDist

        totalWeight += Dist[V];  // 增加最小生成树总权值
        Dist[V] = 0;  // 标记V已收录
        VCount++;  // 更新已收录顶点数

        // 打印收录边E
        cout << Parent[V] << ' ' << V << '\n';

        // 更新V的邻接点的Dist和Parent
        W = Graph->G[V].FirstEdge;  // W为V的邻接点
        while (W != NULL) {  // 遍历V的邻接点W
            if (Dist[W->AdjV] != 0 && W->Weight < Dist[W->AdjV]) {
                // 如果该邻接点W未收录,且W到当前顶点V的距离更小
                // W的父结点为V
                Dist[W->AdjV] = W->Weight;  // 更新Dist[W] 为VW的距离W->Weight
                Parent[W->AdjV] = V;  // 更新Parent
            }
            W = W->Next;
        }
    }

    // 检查是否生成完整的最小生成树(顶点数是否等于原图)
    if (VCount != Graph->Nv) {
        return ERROR;
    }
    return totalWeight;
}

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

    Graph = BuildGraph();  // 读入数据,建立以邻接表为存储结构的图

    ToltalWeight = Prim(Graph, &MST);  // 构建最小生成树MST,并返回总权值

    cout << ToltalWeight << endl;
}
相关推荐
好大哥呀3 小时前
Java Web的学习路径
java·前端·学习
Z1Jxxx4 小时前
01序列01序列
开发语言·c++·算法
梦雨羊5 小时前
Base-NLP学习
人工智能·学习·自然语言处理
丝斯20115 小时前
AI学习笔记整理(42)——NLP之大规模预训练模型Transformer
人工智能·笔记·学习
坚定学代码5 小时前
基于观察者模式的ISO C++信号槽实现
开发语言·c++·观察者模式·ai
小猪佩奇TONY5 小时前
Linux 内核学习(14) --- linux x86-32 虚拟地址空间
linux·学习
副露のmagic6 小时前
更弱智的算法学习 day28
学习
汽车仪器仪表相关领域6 小时前
全自动化精准检测,赋能高效年检——NHD-6108全自动远、近光检测仪项目实战分享
大数据·人工智能·功能测试·算法·安全·自动化·压力测试