Prim算法

本文详细介绍了Prim算法用于求解连通图的最小生成树问题,通过构建优先队列并贪心选择权值最小的边,确保生成树的最小化。算法与Dijkstra算法相比,关注的是每个顶点到已形成树的最短路径而非整个图的最短路径。
摘要由CSDN通过智能技术生成

问题描述

有一张 n n n 个顶点、 m m m 条边的无向图,且是连通图,求最小生成树。

Prim算法简析

P r i m Prim Prim 算法是一种求最小生成树的算法。
设该图为 G = ( V , E ) G = (V, E) G=(V,E)。最小生成树即所求为 G T = ( V T , E T ) G_T = (V_T, E_T) GT=(VT,ET),因为图是连通的,所以最小生成树会覆盖所有的顶点,即 V = = V T V == V_T V==VT G T G_T GT真子图 G A = ( V A , E A ) G_A = (V_A, E_A) GA=(VA,EA),构成一棵树(但 V A < V V_A < V VA<V)。 G − G A G - G_A GGA G B = ( V B , E B ) G_B = (V_B, E_B) GB=(VB,EB), 其中 V B = V − V A V_B = V - V_A VB=VVA
为了这棵树 G A G_A GA 继续生长为最小生成树 G T G_T GT,依据贪心策略,我们要挑选权值最小的边加入 G A G_A GA。也就是说,从连接 G A G_A GA G B G_B GB 的边中挑选权值最小的边。

代码实现

#include <bits/stdc++.h>

using namespace std;

#define MAX 10              // 最大定点数
#define INF 1e8

// 定义边
typedef struct
{
    int to, worth;
} edge;

typedef pair<int, int> P;	    // first -- 顶点编号; second -- 该顶点到树的最短距离

vector<edge> G[MAX];            // 邻接链表
int d[MAX];                     // i 到树的最小距离
bool vis[MAX];					// i 是否在树中

// 最小堆
struct cmp
{
    bool operator()(const P &a, const P &b)
    {
        return a.second > b.second;
    }
};

int prim(void)
{
	// 初始化
    fill(begin(d), end(d), INF);
    d[1] = 0;									// 以 1 为根节点

    priority_queue<P, vector<P>, cmp> Q;
    Q.push(P(1, 0));
    int ans = 0;								// 最小生成树
    while (!Q.empty())
    {
        P p = Q.top();							// p -- 连接 G_A 和 G_B 的最短边
        Q.pop();
        int u = p.first;

        if (vis[u])								// u 是否在树中
            continue;							// 若在,则跳过该点

        ans += d[u];							// 若不在,加入树中
        vis[u] = true;							// u 已加入树

		// 遍历 u 为起点的每一条边
        for (int i = 0; i < G[u].size(); i++)
        {
            edge e = G[u][i];
            if (!vis[e.to] && d[e.to] > e.worth)  // 寻找未加入树且最短的边
            {
                d[e.to] = e.worth;				  // 更新 e.to 到 G_A 的最短路径
                Q.push(P(e.to, e.worth));
            }
        }
    }

    return ans;
}

  • 1、这里我们选择 1 1 1 为根节点。其实,可以选择任意顶点为根节点。因为 G G G 是连通图,所以最终的最小生成树一定覆盖所有的顶点 G . V G.V G.V
  • 2、该算法的关键是如何找到连接 G A G_A GA G B G_B GB 的最短边,这里,我们采用了最小优先队列(以边的权值为排序依据)。对于 G A G_A GA 的一个顶点 u u u,遍历从它的所有边,选择符合要求的边加入 Q Q Q。这样,取出的边就是最短边了。
    我们来分析一下这里的要求 if (!vis[e.to] && d[e.to] > e.worth)。首先,要明确一点,虽然我们在挑选 u u u 的边,但其实,也在挑选边的终点 v v v。因此,顶点 v v v 必须不在 G A G_A GA,这就是条件一。同时, e ( u , v ) e(u, v) e(u,v) 可能有重边,不止一条。所以,我们要挑选重边中 e ( u , v ) . w e(u, v).w e(u,v).w 最小的,这就是条件二
    仔细看这个 i f if if 语句,我们会发现,队列里可能依然有 e ( u , v ) e(u, v) e(u,v) 的重边,但肯定有 e ( u , v ) e(u, v) e(u,v) 的最短边。这里,我们这样处理重边:首先,最小优先队列保证了取出来的一定是最短边;其次,if (vis[u]) 这个判断语句跳过了剩下的重边(因为重边都有一个公共顶点 v v v,然而,由于最小优先队列,它已经加入 G A G_A GA)。
  • 3、我们可以发现,最小生成树算法 P r i m Prim Prim 与最短路径算法 D i j k s t r a Dijkstra Dijkstra 很相似。其中,d[] 的意义并不一样。Prim d [ u ] = d[u]= d[u]= 顶点 u u u G A G_A GA 的最短路径。Dijkstra d [ u ] = d[u]= d[u]= 顶点 u u u 到起点 s s s 的最短路径。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值