版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SZU_Crayon/article/details/82182016
-
最小生成树-Prim(普里姆)算法
-
算法定义:
假设 N=( V,{E} )(V为顶点集,E为边集合)是连通网,TE是N上最小生成树中边的集合,U为N上最小生成树的顶点集
算法从U={u0}(u0为选定的起点,u0∈V),TE={}(空集)开始
在所有 u∈U,v∈V-U 的边(u,v)∈E中,选一条代价最小(u0,v0)(两个集合间能连通的最小距)并入TE,同时 v0加入U,直到U=V(所有顶点连通)
此时TE必有n-1条边,T=( V,{TE} ) 为N的最小生成树
-
算法复杂度:
O(n^2)
-
兄弟算法:Kruskal
Prim用到邻接图,更适用于稠密图
Krukal算法复杂度为O(nlogn),在边数少时效率更高,利用并查集,如果边数多造成树退化,效率会降低,所以对稀疏图的构建速度更快
-
算法理解:
Prim:
无非就是确定一个起点,然后在已经找出的最小生成树顶点中寻找到散点的最小权值来壮大最小生成树。
就像去展会,有N个喜欢的展点,要确定最短参观路线,从门口开始,在自己已经参观的展点中找出离另一个没去过的最近展点,直到全部参观完,当然在这个途中肯定不得避免会经过自己已经参观过的展点,但毕竟是为了下次来更方便
Kruskal:
不同于Prim,Kruskal将顶点间的距离进行排序,将距离近的两个点逐渐合并,直到所有顶点在一个集合为止
-
Prim模板:
#include<iostream>
#include<algorithm>
using namespace std;
using namespace std;
#define INF 1000000007
#define MAX_SIZE 10005
#define Begin 1 //数组开头 1 or 0
// Path[i]->i have lowest path:Path_low_cost[i]
int Path[MAX_SIZE]; //结点i的父亲
int Path_Low_Cost[MAX_SIZE]; //到达结点i的最小值
int Vis[MAX_SIZE]; //标记已经进入最小树的结点
int Min_Cost; //最短路径
//邻接图
struct MGrapth
{
int Vexs[MAX_SIZE]; //顶点表
int Arc[MAX_SIZE][MAX_SIZE]; //邻接矩阵
int Num_Vertext,Num_Edges; //顶点数,边数
};
MGrapth Map;
void Init(int N,int M) //初始化
{
Res=-1;
Min_Cost=0;
Map.Num_Edges=M; //边数
Map.Num_Vertext=N; //顶点数
for(int i=0;i<=N;i++)
for(int j=0;j<=N;j++)
Map.Arc[i][j]=INF; //无穷大初始
for(int i=0;i<=N;i++)
{
Path[i]=Path_Low_Cost[i]=INF;
Map.Vexs[i]=i;
Vis[i]=false;
}
}
void Prim()
{
int Min,i,j,k;
int End=Map.Num_Vertext+1; //结尾
Path[Begin]=Begin; //Begin加入生成树
Path_Low_Cost[Begin]=0;
Vis[Begin]=true; //Begin已经加入
for(i=Begin+1;i<End;i++)
{
Path_Low_Cost[i]=Map.Arc[Begin][i]; //首顶点与其他顶点的距离
Path[i]=Begin;
}
for(int i=Begin+1;i<End;i++)
{
Min=INF;
j=Begin+1,k=Begin;
while(j<End)
{
if(!Vis[j]&&Path_Low_Cost[j]<Min)
{
Min=Path_Low_Cost[j];
k=j;
}
j++;
}
Path_Low_Cost[k]=Min; //保存Path_Low_Cost[k] 到 k 的最短距
Vis[k]=true; //k加入最小生成树
for(j=Begin+1;j<End;j++)
{
if(!Vis[j]&&Map.Arc[k][j]<Path_Low_Cost[j]) //如果 j 不在最小生成树内,并且 k到j有更小的路径,记录,更新
{
Path_Low_Cost[j]=Map.Arc[k][j];
Path[j]=k;
}
}
}
}