Prim最小生成树算法(贪心算法)


最小生成树的性质:
一棵树

  • 没有回路
  • n 个顶点含有 n - 1 条边

生成树

  • 所有顶点都在里面
  • n - 1 条边都在图中
  • 边的权重最小

在生成树的图中任意加一条边都会形成回路
适用于稠密图

Vertex FindMinDist( MGraph Graph, int dist[] )
{ int MinV, V;int MinDist = INFINITY;for (V = 0; V < Graph->Nv; V++){if ( dist[V] != 0 && dist[V] < MinDist){MinDist = dist[V]; //更新最小距离 MinV = V; //更新对应顶点}}if (MinDist < INFINITY) //若找到最小distreturn MinV; else return 0;  // 若这样的顶点不存在
}
int Prim( MGraph Graph, LGraph MST )
{ int dist[MaxVertexNum], TotalWeight = 0; // 初始化权重和 int parent[MaxVertexNum], V, W;int  VCount = 0; //初始化收录的顶点数Edge E;for (V = 0; V < Graph->Nv; V++)// 初始化。默认初始点下标是0 {dist[V] = Graph->G[0][V];//假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY parent[V] = 0;}MST = CreateGraph(Graph->Nv);E = (Edge)malloc( sizeof(struct ENode) ); //建立空的边结点dist[0] = 0;//将初始点0收录进MSTVCount ++;parent[0] = -1; //当前树根是0while (1) {V = FindMinDist( Graph, dist );//未被收录顶点中最小的distif ( V == 0 ) // 若这样的V不存在 break;E->V1 = parent[V];// 将V及相应的边<parent[V], V>收录进MST E->V2 = V;E->Weight = dist[V];InsertEdge( MST, E );TotalWeight += dist[V];dist[V] = 0; //收录进MST VCount++;for( W = 0; W < Graph->Nv; W++ ) //对图中的每个顶点W{if ( dist[W] != 0 && Graph->G[V][W] < INFINITY ) {if ( Graph->G[V][W] < dist[W] ){dist[W] = Graph->G[V][W]; //更新dist[W]parent[W] = V;}}} }if ( VCount < Graph->Nv ) //MST中收的顶点不到|V|个TotalWeight = 0;return TotalWeight;
}


Kruskal算法—将森林合并成树

(来源于中国MOOC-浙大-数据结构)

克鲁斯卡尔算法第一种方法生成最小树

typedef ElementType SetType[MaxVertexNum]; //集合元素下标从0开始
void InitializeVSet( SetType S, int N )
{ ElementType X;for ( X=0; X<N; X++ ) S[X] = -1;
}
void Union( SetType S, int Root1, int Root2 )// /小集合并入大集合
{ if ( S[Root2] < S[Root1] ) { S[Root2] += S[Root1];     S[Root1] = Root2;}else {                        S[Root1] += S[Root2];     S[Root2] = Root1;}
}
int Find( SetType S, ElementType X )
{ if ( S[X] < 0 ) // 找到根return X;elsereturn S[X] = Find( S, S[X] ); // 路径压缩
}
int CheckCycle( SetType VSet, Vertex V1, Vertex V2 )// 检查连V1和V2的边是否在生成树子构成回路
{ Vertex Root1, Root2;Root1 = Find( VSet, V1 );Root2 = Find( VSet, V2 );if( Root1==Root2 ) //若V1和V2已经连通,忽略该边 return 0;else {Union( VSet, Root1, Root2 );return 1;}
}
void PercDown( Edge ESet, int p, int N )//调整最小堆
{ int Parent, Child;struct ENode X;X = ESet[p]; // 取出根结点存放的值for( Parent = p; Parent*2 < N - 1; Parent = Child ) //N - 1条边 {Child = Parent * 2 + 1;if( (Child != N-1) && (ESet[Child].Weight > ESet[Child+1].Weight) ) // Child指向左右子结点的较小者Child++; if( X.Weight <= ESet[Child].Weight ) break;elseESet[Parent] = ESet[Child];}ESet[Parent] = X;
}
void InitializeESet( LGraph Graph, Edge ESet )
{ Vertex V;PtrToAdjVNode W;int ECount;ECount = 0;for ( V = 0; V < Graph->Nv; V++ ){for ( W = Graph->G[V].FirstEdge; W; W = W->Next ){      if ( V < W->AdjV ) //避免重复录入无向图的边,只收V1<V2的边{  ESet[ECount].V1 = V;ESet[ECount].V2 = W->AdjV;ESet[ECount++].Weight = W->Weight;}}} for ( ECount = Graph->Ne/2; ECount >= 0; ECount-- )//形成最小堆 PercDown( ESet, ECount, Graph->Ne );
}
int GetEdge( Edge ESet, int CurrentSize )//给定当前堆的大小CurrentSize,将当前最小边位置弹出并调整堆
{ Swap( &ESet[0], &ESet[CurrentSize-1]);   //将最小边与当前堆的最后一个位置的边交换PercDown( ESet, 0, CurrentSize-1 );  //将剩下的边继续调整成最小堆return CurrentSize-1; // 返回最小边所在位置
}
int Kruskal( LGraph Graph, LGraph MST )
{WeightType TotalWeight;int ECount, NextEdge;SetType VSet; //顶点数组Edge ESet;    //边数组InitializeVSet( VSet, Graph->Nv ); //初始化顶点并查集ESet = (Edge)malloc( sizeof(struct ENode)*Graph->Ne );InitializeESet( Graph, ESet );//初始化边的最小堆MST = CreateGraph(Graph->Nv);TotalWeight = 0;ECount = 0; NextEdge = Graph->Ne; //原始边集的规模while ( ECount < Graph->Nv-1 )// 当收集的边不足以构成树时{  NextEdge = GetEdge( ESet, NextEdge ); //从边集中得到最小边的位置if (NextEdge < 0) //边集已空break;if ( CheckCycle( VSet, ESet[NextEdge].V1, ESet[NextEdge].V2 )==true )//如果该边的加入不构成回路,即两端结点不属于同一连通集{InsertEdge( MST, ESet+NextEdge );TotalWeight += ESet[NextEdge].Weight; //累计权重 ECount++; // 生成树中边数加1}}if ( ECount < Graph->Nv-1 )TotalWeight = -1; //表示生成树不存在return TotalWeight;
}


克鲁斯卡尔算法另外一种方法生成最小树(来源于清华出版社-胡昭民著的《数据结构》)
思维导图

该方法和上一个最不一样的地方在于该方法判断是否形成回路时,将被纳入集合树的每个边的两端点进行计数,v[mceptr->from]++;//该点经过次数 v[mceptr->to]++;//该点经过次数,那么只要两者同时大于等于2,那么肯定是有回路形成,可以自己想象。

#include <stdio.h>
#include <stdlib.h>
#define VERTS 6                /*图的顶点数*/
struct edge                      /*声明边的结构*/
{  int from,to;int find,val;struct edge* next;
};
typedef struct edge node;
typedef node* mst;
int v[VERTS+1];
mst findmincost(mst head)                 /*所示成本最小的边*/
{int minval = 100;mst ptr,retptr;ptr = head;while(ptr != NULL){if(ptr->val < minval && ptr->find==0){                                  /*假如ptr->val的值小于minval*/minval = ptr->val;             /*就把ptr->val设为最小值*/retptr = ptr;                    /*并且把ptr记录下来*/}ptr=ptr->next;}retptr->find = 1;                        /*将retptr设为已找到的边*/return retptr;                         /*返回retptr*/
}
void mintree(mst head)                     /*最小成本生成树函数*/
{mst ptr,mceptr;int i,result = 0;ptr = head;for(i = 0;i <= VERTS;i++)v[i] = 0;while(ptr != NULL){mceptr = findmincost(head);v[mceptr->from]++;//该点经过次数 v[mceptr->to]++;//该点经过次数 if(v[ mceptr->from ] > 1 && v[ mceptr->to ] > 1 ) //防止出现闭合的回路 {v[mceptr->from]--;v[mceptr->to]--;result = 1;//直接舍去,到该循环的最后一步ptr=ptr->next; }elseresult = 0;if(result == 0)printf("起始顶点 [%d] -> 终止顶点 [%d] -> 路径长度 [%d]\n",mceptr->from,mceptr->to,mceptr->val);ptr = ptr->next;//整个顶点数等于遍历次数,保证每个点都在里面 }
}
int main()
{  int data[10][3]={{1,2,6},{1,6,12},{1,5,10},{2,3,3},    /*成本表数组*/{2,4,5},{2,6,8 },{3,4,7},{4,6,11},{4,5,9},{5,6,16}};int i,j;mst head,ptr,newnode;head = NULL;  for(i = 0;i < 10;i++)                    /*建立图的链表*/{  for(j = 1;j <= VERTS;j++){  if(data[i][0] == j){  newnode = (mst)malloc(sizeof(node));newnode->from = data[i][0];newnode->to = data[i][1];newnode->val = data[i][2];newnode->find = 0;newnode->next = NULL;if(head == NULL){  head = newnode;head->next = NULL;ptr = head;}   else{  ptr->next = newnode;ptr = ptr->next;}}}}mintree(head);                        /*建立最小成本生成树*/return 0;
}

c++语言实现:

#include<iostream>
using namespace std;
#include<cstdio>
#include<algorithm>
const int N = 100;
int nodeset[N];
int n,m;
struct Edge{int v;//起始点 int u;//终端点 int w;//权重
}e[N*N]; //邻接矩阵
bool comp(Edge x,Edge y)
{return x.w < y.w;//升序排序
}
void Init(int n)
{for(int i = 1;i <= n;i++)nodeset[i] = i;//初始化为自身
}
int Find(int n)//寻找集合点根
{if(nodeset[n] != n)nodeset[n] = Find(nodeset[n]);return nodeset[n];
}
int Merge(int a,int b)
{int p = Find(a);int q = Find(b);if(p == q)//形成必和圈 return 0;if(p > q) //小的集合号赋给大的集合号 nodeset[p] = q;elsenodeset[q] = p;return 1;
}
int Kruskal(int n)
{int ans = 0;for(int i = 0;i < m;i++){if(Merge(e[i].v,e[i].u) != 0) //未形成闭合圈 { ans += e[i].w;//权重累计和 n--;if(n == 1)//只有一个点时 return ans;}}return 0;
}
int main()
{cout<<"输入节点数n和边数m: "<<endl;cin>>n>>m;Init(n);cout<<"输入结点数v,u和边值w: "<<endl;for(int i = 0;i < m;i++)cin>>e[i].v>>e[i].u>>e[i].w;sort(e,e+m,comp); //库函数排序 int ans = Kruskal(n);cout<<"最小的花费是: "<<ans<<endl;return 0;
}

(浙大-19-夏-数据结构)Prim(普里姆算法)和Kruskal(克鲁斯卡尔算法)最小生成树相关推荐

  1. JavaScript实现prim普里姆算法(附完整源码)

    JavaScript实现prim普里姆算法(附完整源码) Comparator.js完整源代码 Heap.js完整源代码 MinHeap.js完整源代码 PriorityQueue.js完整源代码 G ...

  2. 644-最小生成树Prim普里姆算法

    最小生成树Prim普里姆算法理论 最小生成树的应用场景:顶点代表城市的话,比如说修路,架设电线,网络,怎么让这几个城市连起来,而且花费是最小的,成本最低,权值是最小的,但是不允许形成环路. 第一步的U ...

  3. Prim 普里姆算法(邻接矩阵)C语言版

    Prim算法的思维路线较为简单,百度基本都能看懂.但是编程还是有点难的. 整个围绕两个东西转,一个lowcost数组,一个adjvex数组,前者是用来保存当前生成树到其它顶点的权值的. 比如这个图,假 ...

  4. 最小生成树(普里姆算法【Prim】与克鲁斯卡尔算法【Kruskal】)

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  5. 【数据结构】——图的最小生成树算法(普里姆+克鲁斯卡尔)

    这里的图指的是带权无向图,也就是无向网. 关于最小生成树 图的最小生成树要解决的问题:用最小的代价连通图中的所有顶点. 下面两种算法都是运用贪心思想,利用MST(Minimum Spanning Tr ...

  6. 图的连通法之普里姆算法和卡鲁斯卡尔算法

    最小生成树 连通图:图的连通其实就是树,图的最小连通图其实就是最小生成树. 树:如果一个无向连通图中不存在回路,则这种图称为树. 生成树:无向连通图G的一个子图如果是一颗包含G的所有顶点的树,则该子图 ...

  7. 【HDU - 1301】Jungle Roads(并查集+最小生成树)(内附最小生成树两种算法 克鲁斯特尔算法amp;amp;普里姆算法)

    题干: Jungle Roads Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  8. 用c语言描述普里姆算法和克鲁斯卡尔算法,克鲁斯卡尔算法+普里姆算法 详解

    克鲁斯卡尔算法: [1]克鲁斯卡尔算法 普里姆算法是以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树. 克鲁斯卡尔算法是直接以边为目标去构建. 因为权值是在边上,直接去找最小权值的边来构建生 ...

  9. 克鲁斯卡尔算法(Kruskai)和普里姆算法(Prim)

    动画参考视频:最小生成树(Kruskal(克鲁斯卡尔)和Prim(普里姆))算法动画演示_哔哩哔哩_bilibili 克鲁斯卡尔算法(Kruskai) 克鲁斯卡尔算法,从边的角度求网的最小生成树,时间 ...

最新文章

  1. 深入理解Netty-从偶现宕机看Netty流量控制
  2. 定时器0工作方式1,定时1s
  3. SQL转换函数to_char/to_date/to_number
  4. 如何通过JavaScript动态加载js
  5. server2008R2平台部署exchange2010
  6. TIMEOUT will also publish one order event
  7. SQL Server 兼容模式
  8. 【Python-GPU】GPU数据科学加速包——RAPIDS
  9. Python访问PostGIS(建表、空间索引、分区表)
  10. 为什么都开始流行将洗手台装在厕所外?
  11. 那个名为 XROS 的操作系统,倒在了元宇宙浪潮中!
  12. qt中实现左右分割线_一种快速刷新richedit中内嵌动画的方法的实现
  13. java给方法参数赋值_java方法参数的赋值问题实例源码介绍
  14. PPT的配色方法(师从于珞珈老师)
  15. [大洋] Unity3D架构系列之- FSM有限状态机设计一至四
  16. go build报错,提示错误can‘t load package: package xxx is not in GOROOT (D:\Go\Go\src\xxx)
  17. 21届校招中获得12家公司offer的一些经验(包括6家银行信息科技岗offer)
  18. 关于Android自启动管理的相关知识点
  19. Android开发之智能聊天机器人
  20. 使用python快速搭建接口自动化测试脚本实战总结

热门文章

  1. 深澜系统服务器架构,深澜认证服务器
  2. 组织机构或家谱前端设计
  3. 踏入社会需要懂得的道理
  4. 基于KNN的newsgroup 18828文本分类器的Python实现
  5. python不支持的数据类型有achar bint cfloat dlist_python不支持的数据类型有achar bint cfloat dlist_DM 类数据类型...
  6. 【一本通评测 1371】【堆】看病
  7. Edge Detector ----------Marr Hildreth 算法
  8. CGAL学习记录——网格孔洞填充
  9. 在多媒体计算机系统中,不能用以存储多媒体信息的是【 】.,在多媒体计算机系统中,不能用以存储多媒体信息的是______。...
  10. 基于深度学习的垃圾分类以及生活垃圾产出量的可视化