PTA 最小生成树与拓扑排序

本文介绍了最小生成树的概念及其两种主要算法:Prim算法和Kruskal算法,通过实例展示了如何应用这两种算法解决最小生成树问题。同时,还探讨了拓扑排序在关键路径问题中的应用,提供了具体的案例分析和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小生成树
特点:
1.是一棵树。 无回路,N个顶点有N-1条边。
2.是生成树。 包含全部顶点,N-1条边都在图里。
3.边的权重和最小。

主要包括两种算法,一种是让小树慢慢长大的Prim算法(先定一个顶点为起点,然后每次都找到离这棵树最近的那个顶点,将他归进树内,直到正好用掉顶点数N-1条边)。
二是Kruskal算法,将一个个森林(一开始每个节点都是森林)连成树。每次在图中找所有的边中权重最小的那个边,将其两端的顶点相连(如果产生回路则不收录这条边,并彻底忽略它),直到找满了N-1条边。

Prime模板

/* 邻接矩阵存储 - Prim最小生成树算法 */
 
Vertex FindMinDist( MGraph Graph, WeightType dist[] )
{
   
    /* 返回未被收录顶点中dist最小者 */
    Vertex MinV, V;
    WeightType MinDist = INFINITY;
 
    for (V=0; V<Graph->Nv; V++) {
   
   
        if ( dist[V]!=0 && dist[V]<MinDist) {
   
   
            /* 若V未被收录,且dist[V]更小 */
            MinDist = dist[V]; /* 更新最小距离 */
            MinV = V; /* 更新对应顶点 */
        }
    }
    if (MinDist < INFINITY) /* 若找到最小dist */
        return MinV; /* 返回对应的顶点下标 */
    else return ERROR;  /* 若这样的顶点不存在,返回-1作为标记 */
}
 
int Prim( MGraph Graph, LGraph MST )
{
   
    /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
    WeightType dist[MaxVertexNum], TotalWeight;
    Vertex parent[MaxVertexNum], V, W;
    int VCount;
    Edge E;
     
    /* 初始化。默认初始点下标是0 */
       for (V=0; V<Graph->Nv; V++) {
   
   
        /* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */
           dist[V] = Graph->G[0][V];
           parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0 */ 
    }
    TotalWeight = 0; /* 初始化权重和     */
    VCount = 0;      /* 初始化收录的顶点数 */
    /* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
    MST = CreateGraph(Graph->Nv);
    E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 */
            
    /* 将初始点0收录进MST */
    dist[0] = 0;
    VCount ++;
    parent[0] = -1; /* 当前树根是0 */
 
    while (1) {
   
   
        V = FindMinDist( Graph, dist );
        /* V = 未被收录顶点中dist最小者 */
        if ( V==ERROR ) /* 若这样的V不存在 */
            break;   /* 算法结束 */
             
        /* 将V及相应的边<parent[V], V>收录进MST */
        E->V1 = parent[V];
        E->V2 = V;
        E->Weight = dist[V];
        InsertEdge( MST, E );
        TotalWeight += dist[V];
        dist[V] = 0;
        VCount++;
         
        for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
            if ( dist[W]!=0 && Graph->G[V][W]<INFINITY ) {
   
   
            /* 若W是V的邻接点并且未被收录 */
                if ( Graph->G[V][W] < dist[W] ) {
   
   
                /* 若收录V使得dist[W]变小 */
                    dist[W] = Graph->G[V][W]; /* 更新dist[W] */
                    parent[W] = V; /* 更新树 */
                }
            }
    } /* while结束*/
    if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */
       TotalWeight = ERROR;
    return TotalWeight;   /* 算法执行完毕,返回最小权重和或错误标记 */
}

Kruskal模板

/* 邻接表存储 - Kruskal最小生成树算法 */
 
/*-------------------- 顶点并查集定义 --------------------*/
typedef Vertex ElementType; /* 默认元素可以用非负整数表示 */
typedef Vertex SetName;     /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MaxVertexNum]; /* 假设集合元素下标从0开始 */
 
void InitializeVSet( SetType S, int N )
{
   
    /* 初始化并查集 */
    ElementType X;
 
    for ( X=0; X<N; X++ ) S[X] = -1;
}
 
void Union( SetType S, SetName Root1, SetName Root2 )
{
   
    /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] ) {
   
    /* 如果集合2比较大 */
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else {
   
                            /* 如果集合1比较大 */
        S[Root1
### PTA 平台最小生成树简单题目解法 #### 使用Kruskal算法求解最小生成树 对于PTA平台上涉及最小生成树的简单题目,通常可以采用Kruskal算法来解决。该算法基于贪心策略,每次选取权重最小且不会形成环路的边加入到当前森林中直到构建完成一棵包含所有顶点的树[^1]。 ```c #include <stdio.h> #define MAXVEX 100 /*最大顶点数*/ typedef struct { int u, v; /*边的两个端点编号 */ int weight; /* 边权值 */ } Edge; Edge edges[MAXVEX]; /* 存储图中的每条边 */ int parent[MAXVEX]; /* 查找函数并做路径压缩 */ int find(int i) { while (parent[i] > 0) i = parent[i]; return i; } void Kruskal(int n, int e) { int i, j, k = 0; int s1, s2; // 初始化集合 for(i=0;i<n;i++) parent[i]=0; qsort(edges,e,sizeof(Edge),cmp); printf("Minimum Spanning Tree Edges:\n"); for(i=0,k=0;k<n-1&&i<e;i++) { s1=find(edges[i].u); s2=find(edges[i].v); if(s1 != s2){ parent[s1]=s2; printf("(%d,%d)\n",edges[i].u,edges[i].v); k++; } } } ``` 此代码片段展示了如何利用Kruskal算法计算给定无向连通网G=(V,E)的一棵最小代价生成树T=(U,T),其中`qsort()`用于对所有的边按照其长度从小到大排序;而`find()`实现了带路径压缩技术的查找操作以提高效率。 #### Prim算法实现最小生成树 除了Kruskal之外,在某些情况下Prim算法也是处理这类问题的有效方法之一。这种方法适用于稠密图,并通过不断扩展已有的部分生成树来进行工作: ```c // 定义无穷大常量 const int INF = 99999999; void prim(int cost[][MAX], int start, int numVertices) { bool visited[numVertices]; int lowCost[numVertices]; int closestVertex[numVertices]; fill_n(lowCost, numVertices, INF); memset(closestVertex, -1, sizeof(closestVertex)); memset(visited, false, sizeof(visited)); lowCost[start] = 0; for (int count = 0; count < numVertices; ++count) { int minDist = INF, vertexToVisit = -1; for (int i = 0; i < numVertices; ++i) { if (!visited[i] && lowCost[i] < minDist) { minDist = lowCost[i]; vertexToVisit = i; } } if(vertexToVisit == -1 || minDist == INF) break; visited[vertexToVisit] = true; for (int adjVertex = 0; adjVertex < numVertices; ++adjVertex) { if(!visited[adjVertex] && cost[vertexToVisit][adjVertex] < lowCost[adjVertex]) { lowCost[adjVertex] = cost[vertexToVisit][adjVertex]; closestVertex[adjVertex] = vertexToVisit; } } } // 输出结果... } ``` 这段程序定义了一个名为prim()的方法,它接收邻接矩阵形式的成本表作为输入参数,并返回由选定起点出发所形成的最小生成树的信息。这里采用了优先队列优化版本的思想简化了原始逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值