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

2025博客之星年度评选已开启 10w+人浏览 3.6k人参与

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

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

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

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

例如:

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号顶点在同一个集合中。

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

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

0 1
1 3
1 2
7

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

3 3
0 1 10
0 2 30
1 2 25

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

0 1
1 2
35

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

C++代码

#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;
}
### PTA 6.2 使用Prim算法构建最小生成树 #### 解决方案概述 为了理解如何使用Prim算法来解决PTA 6.2中的最小生成树(MST)问题,先要明白该算法的核心概念。最小生成树是一个连接图中所有顶点而不形成任何环路的子集,并使总的边权重达到最小[^2]。 #### Prim算法流程描述 在执行Prim算法的过程中,会区分两种类型的节点:已加入到正在生长的最小生成树中的白点(即已经处理过的节点),以及尚未被访问的蓝点(未处理的其他节点)。每次迭代时,都会寻找从当前集合V_new出发到达最近的一个新节点之间的最短边缘作为下一步扩展的对象[^3]。 #### 实现细节与伪代码展示 下面给出一段Python实现版本的Prim算法用于求解给定无向加权连通图G=(V,E)上的MST: ```python import heapq def prim_algorithm(graph, start_vertex): mst = [] visited = {start_vertex} edges = [(cost, start_vertex, to) for to, cost in graph[start_vertex].items()] heapq.heapify(edges) while edges: cost, frm, to = heapq.heappop(edges) if to not in visited: visited.add(to) mst.append((frm, to, cost)) for next_node, weight in graph[to].items(): if next_node not in visited: heapq.heappush(edges, (weight, to, next_node)) return mst ``` 这段代码通过优先队列维护候选边列表并不断选取其中代价最低者直至遍历完所有顶点为止。注意这里假设输入`graph`是以邻接表形式表示的一张带权有向图,键为起点而值则是一字典记录着指向终点及其对应的成本信息。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qhumaing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值