生成树概念: 任何只由图G的边构成,并包含所有顶点的树称为G的生成树
最小生成树概念:最小生成树是其所有生成树中权重最小的生成树
算法区别
普里姆算法:普里姆算法贪的是点,适用于点少边多的稠密图,从不在点集合S的点中选出一个点,假设选出的点是j,我们让他与S内的某点距离最短,这样我们选出了一条生成树上的边,同时将点j加入S中,不断重复这个过程,直到所有点都加入点集合S。
克鲁斯卡尔算法:克鲁斯卡尔算法贪的是边,适用于点多边少的稀疏图,核心代码是用的是并查集思想,我们首先将边按照权值从小到大(利用结构体内嵌比较函数)排序后逐个判断,如果当前的边加入后不会产生环,那么就把当前边作为生成树的一条边,重复此过程,一共选取(V-1)条边,V为顶点数,最终得到的就是最小生成树
在编写普里姆算法的代码之前我们要知道普里姆算法需要哪些东西
- 首先要初始化图中两点之间距离无穷大
- 用closeset[maxn]记录不在S中的顶点在S中的最近邻接点
- 用lowcost[maxn]记录不在S中的顶点到S的最短距离,即到最近邻接点的权值
- 用vis[maxn]标记顶点是否被访问
时间复杂度分析:
T(n)=O(V^2)[邻接矩阵]——>稠密图
堆优化(小根堆):T(n)=O(VlogV)+O(ElogV)=O(ElogV)
堆优化(斐波那契堆):T(n)=O(E+VlogV)
下面通过题目来理解普里姆算法
题目描述
使用Prim算法求图的最小生成树(MST)
输入
每组数据分为两个部分,第一部分为图的点数n,和边数m,
第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。
输出
最小生成树,输出时按照边的两个端点的升序输出。(先看左端点,再看右端点,端点不换位置)
样例输入
3 3
0 1 10
0 2 15
1 2 50
样例输出
0 1 10
0 2 15
ps:1.这是一个最小生成树的模板题,要注意的就是得按边升序输出
普里姆算法是一种基于贪心思想求解加权无向图的最小生成树的算法
AC代码——普通版普里姆
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3 + 10;
struct node
{
int from, to, dis;
bool operator<(const node x) const
{
if (from != x.from)
return from < x.from;
return to < x.to;
}
} ans[maxn], w[maxn][maxn];
int pre[maxn], dis[maxn], vis[maxn], cnt, n;
//pre[i]表示dis的前一个点 dis[i]表示已经确定的点集到i的距离
void prim()
{
memset(dis, inf, sizeof(dis)); //初始化距离
memset(vis, 0, sizeof(vis)); //初始化标记
node now, ne;
dis[0] = 0, vis[0] = 1; //把第一个点加到S中
for (int i = 0; i < n; ++i) //初始化点集S中只有一个点0
if (w[0][i].dis != inf)
dis[i] = w[0][i].dis, pre[i] = 0; //先将与0相连的点的距离处理
for (int i = 1; i < n; ++i)
{
//选剩下的n-1个点,每次循环找出一个到S距离最近的顶点
int minn = inf, id = 0;
//每一次循环计算所有没有使用的顶点到当前S的距离,得到在没有使用的顶点中到S的最短距离以及顶点号
for (int j = 0; j < n; ++j)
{
if (!vis[j] && dis[j] < minn