c语言贪心算法最小生成树,贪心算法4-最小生成树(Prim算法)

1460000021555890

1.问题分析

在一个有n个节点的无向连通图G = (V, E)中,V表示顶点集,E表示边集。只需n-1条边就可以使这个图连通,n-1条边要想保证图连通,就必须不含回路,所以我们只需要找出n-1条权值最小且无回路的边即可。

bVbCAVN

需要明确几个概念:

生成子图:选中一些边和所有顶点组成的图,称为原图的生成子图。

生成树:如果生成子图恰好是一棵树,称为生成树。

最小生成树:权值之和最小的生成树,称为最小生成树。

2.算法分析

为了在最小生成树的生成过程中,不产生环路,我们可以使用“切割法”。具体来说,在生成树的过程中,我们把已经在生成树的节点看成一个集合,把剩下的节点看作另一个集合,在这两个集合之间画一条切割线,从切割线经过的边上选出一条取值最小的作为新加入的边,可以形象地把这种方法称为“切割法”。

首先任选一个节点,如1号节点,把它放在U中,U = {1},那么剩下的节点即V - U = {2,3,4,5,6,7},V是图的所有顶点集合。如图2所示。

bVbCAWP

现在在连接两个集合(U和V-U)的边中找出权值最小的,通过画切割线可以很快找到节点1和节点2之间的边权值最小,选中这条边,把2号节点加入U = {1, 2}, V - U = {3, 4, 5, 6}。

再按照上述操作在连接两个集合(U和V-U)的边中找出权值最小的边,如图3所示。如此下去,直到U = V结束。

bVbCAXN

这个就是Prim算法,1957年由美国计算机科学家Robert C.Prim发现的。通过观察可以发现,Prim算法的贪心策略是:每次选取连接U和V-U的所有边中的最短边。

3.算法设计

算法设计的步骤如下所示:

步骤1:设计数据结构。用带权邻接矩阵C存储图G,bool数组s[],如果s[i] = true,说明顶点i已加入集合U,如图2所示。还有一个问题就是,从图上我们可以很直观地找出连接两个集合中的权值最小的边,但是在程序中如果穷举这些边就会很麻烦。在单源最短路径,我们只需要维护一个源点到其它点的最短距离数组dist[]即可,但是这里显然不行,我们需要维护的是V-U中的点到U的最短距离,需要两个数组,closest[j]表示V-U中的节点j到集合U中的最临近点,lowcost[j]表示这两个点之间边的权值。对于图2的求解过程,对应的closest[]和lowcost[]如下图所示:

bVbCBtd

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贪心算法最小生成树是指通过不断地选择当前状态下的最优解,最终得到全局最优解的一种算法。在最小生成树问题中,我们需要找到一棵包含所有节点的树,使得树上边的权值之和最小。 以下是使用 Prim 算法实现最小生成树的 C 语言代码: ``` #include <stdio.h> #include <limits.h> #define V 5 //节点数 int minKey(int key[], bool mstSet[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) if (mstSet[v] == false && key[v] < min) min = key[v], min_index = v; return min_index; } void printMST(int parent[], int graph[V][V]) { printf("Edge \tWeight\n"); for (int i = 1; i < V; i++) printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]); } void primMST(int graph[V][V]) { int parent[V]; int key[V]; bool mstSet[V]; for (int i = 0; i < V; i++) key[i] = INT_MAX, mstSet[i] = false; key[0] = 0; parent[0] = -1; for (int count = 0; count < V - 1; count++) { int u = minKey(key, mstSet); mstSet[u] = true; for (int v = 0; v < V; v++) if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) parent[v] = u, key[v] = graph[u][v]; } printMST(parent, graph); } int main() { int graph[V][V] = { { 0, 2, 0, 6, 0 }, { 2, 0, 3, 8, 5 }, { 0, 3, 0, 0, 7 }, { 6, 8, 0, 0, 9 }, { 0, 5, 7, 9, 0 } }; primMST(graph); return 0; } ``` 在这个实现中,我们使用了一个 key 数组来存储每个节点到最小生成树的最小边权值,一个 mstSet 数组来标记每个节点是否已经加入最小生成树,以及一个 parent 数组来记录每个节点的父节点。在每次迭代中,我们选取当前 key 值最小的节点 u,将它标记为加入最小生成树,然后更新它所有的邻居节点的 key 值和 parent 数组。最终,我们遍历 parent 数组来输出最小生成树的边。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值