最小生成树-prime算法

本文详细介绍了普利姆算法在求解最小生成树问题中的应用。通过实例演示了算法的具体步骤,包括初始化、选取起始顶点、迭代寻找最短边并更新顶点集直至覆盖所有顶点的过程。

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

Prime算法的核心步骤是:在带权连通图中V是包含所有顶点的集合, U已经在最小生成树中的节点,从图中任意某一顶点v开始,此时集合U={v},重复执行下述操作:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边,将(u,w)这条边加入到已找到边的集合,并且将点w加入到集合U中,当U=V时,就找到了这颗最小生成树。

       其实,算法的核心步骤就是:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边。

      知道了普利姆算法的核心步骤,下面我就用图示法来演示一下工作流程,如图:

首先,确定起始顶点。我以顶点A作为起始点。根据查找法则,与点A相邻的点有点B和点H,比较AB与AH,我们选择点B,如下图。并将点B加入到U中。

继续下一步,此时集合U中有{A,B}两个点,再分别以这两点为起始点,根据查找法则,找到边BC(当有多条边权值相等时,可选任意一条),如下图。并将点C加入到U中。

继续,此时集合U中有{A,B,C}三个点,根据查找法则,我们找到了符合要求的边CI,如下图。并将点I加入到U中。

继续,此时集合U中有{A,B,C,I}四个点,根绝查找法则,找到符合要求的边CF,如下图。并将点F加入到集合U中。

继续,依照查找法则我们找到边FG,如下图。并将点G加入到U中。

继续,依照查找法则我们找到边GH,如下图。并将点H加入到U中。

继续,依照查找法则我们找到边CD,如下图。并将点D加入到U中。

继续,依照查找法则我们找到边DE,如下图。并将点E加入到U中。

此时,满足U = V,即找到了这颗最小生成树。

同样, 我继续用POJ 2395这题为例子给出代码:

 

#include <iostream>
#include <string.h>
 
using namespace std;
const int MAXN = 2010;
const int INF = 1 << 30;
int map[MAXN][MAXN];
int N, M;
int lowcost[MAXN];
 
void init()
{
  for(size_t i = 0; i <= N; ++i)
    for(size_t j = 0; j <= N; ++j)
      map[i][j] = INF;
}
 
int prime()
{
  for(size_t i = 1; i <= N; ++i)
    lowcost[i] = map[1][i];
  int min;
  bool visited[N + 1];// index begin from 1 not 0
  int ans = -1;
  memset(visited, false, sizeof(visited));
  lowcost[1] = 0;
  visited[1] = true;
  for(size_t i = 1; i < N; ++i)//loop N - 1 times
  {
    min = INF;
    int k;
    for(size_t j = 1; j <= N; ++j)// find the minimun edge between two edge set
    {
      if(!visited[j] && min > lowcost[j])
      {
        min = lowcost[j];
        k = j;
      }
    }
    visited[k] = true;
    ans = ans > min ? ans : min; 
    for(size_t j = 1; j <= N; ++j)// update the array of lowcost 
    {
      if(!visited[j] && lowcost[j] > map[k][j])
        lowcost[j] = map[k][j];
    }
  }
  return ans;
}
 
int main()
{
  while(cin >> N >> M)
  {
    init();
    int x, y, c;
    for(size_t i = 0; i < M; ++i)
    {
      cin >> x >> y >> c;
      if(map[x][y] > c)
        map[x][y] = c;
      if(map[y][x] > c)
        map[y][x] = c;
    }
    cout << prime() << endl;
  }
}

 

### 关于最小生成树的Prim算法 #### Prim算法的核心概念 Prim算法是一种用于解加权连通图中最小生成树的经典贪心算法。其基本思想是从任意一个顶点开始构建一颗树,逐步扩展这棵树直到覆盖所有的顶点[^1]。在此过程中,始终选择当前未加入树的顶点中最短的一条边来扩展树。 #### Prim算法的具体步骤 Prim算法通过维护两个集合完成计算:一个是已经加入最小生成树顶点集合 \( S \),另一个是尚未加入的顶点集合 \( V-S \)[^2]。每次从未加入的顶点集中选取一条与已加入顶点集相邻且权重最小的边,并将对应的顶点纳入\( S \)中。这一过程持续进行,直到所有顶点都加入到\( S \)中为止。 #### Prim算法的时间复杂度分析 Prim算法的时间复杂度主要取决于如何存储和操作邻接关系以及优先队列的选择方式。当采用邻接矩阵表示时,时间复杂度为 \( O(V^2) \); 若使用二叉堆优化并配合邻接表,则可以降低至 \( O((V+E)\log V) \), 其中 \( V \) 是顶点数量,\( E \) 是边的数量[^3]。 #### C语言实现示例 以下是基于邻接矩阵的一个简单版本Prim算法实现: ```c #include <stdio.h> #define INF 9999999 #define MAX_VERTICES 100 int minKey(int key[], int mstSet[], int V){ int min = INF, min_index; for (int v = 0; v < V; v++) { if (!mstSet[v] && key[v] < min){ min = key[v]; min_index = v; } } return min_index; } void primMST(int graph[MAX_VERTICES][MAX_VERTICES], int V){ int parent[V]; int key[V]; int mstSet[V]; for (int i = 0; i < V; i++){ key[i] = INF; mstSet[i] = 0; } key[0] = 0; parent[0] = -1; for (int count = 0; count < V-1; count++){ int u = minKey(key, mstSet, V); mstSet[u] = 1; for (int v = 0; v < V; v++) if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v]){ parent[v] = u; key[v] = graph[u][v]; } } printf("Edge\tWeight\n"); for (int i = 1; i < V; i++) printf("%d-%d\t%d\n", parent[i], i, graph[parent[i]][i]); } ``` 该程序定义了一个函数`primMST`, 它接收一个二维数组形式的邻接矩阵作为输入参数,并打印出构成最小生成树的所有边及其权重[^4]。 #### 总结 Prim算法提供了一种有效的方法来解决无向带权图上的最小生成树问题。它利用贪婪策略不断挑选最优局部决策从而达到全局最优目标。尽管存在其他替代方案如Kruskal算法,但在稠密图场景下,Prim通常表现更好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值