最小生成树——普利姆 克鲁斯卡尔

最小生成树

生成树定义:是原图的一个极小连通子图,含有原图的全部顶点,但只有n-1条边。它连通但边只有n-1,也就是说任意让两点连边必定成环,不过这结论好像没啥用。

最小生成树:对于一张图的生成树可能有多种,对于边权和最小的一种就是最小生成树了。

prim算法

首先首先,我们来几个标识,原图是N={V,E},TE是N上最小生成树的边集,然后有个已选点集合U,那么未选点集就是V-U了。

首先,先来个起点,加入到U里面。

然后,在所以边(a,b),a属于U,b属于V-U里面,找一条权值最小的边(a0,b0),并入集合TE,同时b0加入到U。

最终,经过不断的重复,U越来越多,达到n个点,也就是说U=V;

证明:    设prim生成的图为G0

    假设存在Gmin使得cost(Gmin)<cost(G0),则Gmin存在边<u,v>不属于G0;

    将<u,v>加入Gmink可以得到一个环,且<u,v>不是该环的最长边

    这与prim每次生成最短边矛盾;

prim的实现:

  首先会用到closedge结构体数组,包括adjvex域和lowcost域;

struct node{
    int adjvex;
    int lowcost;
}closedge[maxn];   //closedge[i]中,i和closedge[i].adjvex组成一条边,权值为lowcost
//整个数组用于记录U到V-U具最小权值的边,每次选最小的就是上面的步骤二了

先来看看算法分析:

struct node{
    int adjvex;
    int lowcost;//一开始初始化为正无穷
}closedge[maxn];   //closedge[i]中,i和closedge[i].adjvex组成一条边,权值为lowcost
//整个数组用于记录U到V-U具最小权值的边,每次选最小的就是上面的步骤二了

void prim(AMGRAPH G, char u)
{//以名字u为起点,在邻接矩阵表示的图G中,构建最小生成树
    int k=locate(G,u);//寻找u名字在定点表中的下标
    for(int i=0;i<n;i++)
        if(i!=k) closedge[i]={u,G.arc[k][j]};//对起点的邻接点初始化
    closedge[k].lowcost =0;//为0就代表着选过了
    for(int i=1;i<G.vexnum;i++)
    {//选择其余n-1个点
        k=min(closedge);//找出closedge中lowcost最小的编号(下标)
        int u0=closedge[k].adjvex;//属于U
        int v0=G.vexs[k];         //属于V-U
        cout<<u0<<v0;
        closedge[k].lowcost =0;
        for(int j=0;j<G.vexnum;++j){
            if(G.arc[k][j]<closedge[j].lowcost )
                closedge[j].lowcost=G.arc[k][j];
                closedge[j].adjvex =G.vex[k];
        }
    }
}

正式代码:

 1 struct node{
 2     int adjvex; /*或者*/ char adjvex;//一个是编号一个是名字
 3     int lowcost;//一开始初始化为正无穷
 4 }closedge[maxn];   //closedge[i]中,i和closedge[i].adjvex组成一条边,权值为lowcost
 5 //整个数组用于记录U到V-U具最小权值的边,每次选最小的就是上面的步骤二了
 6
 7 int locate(AMGRAPH G,char u)
 8 {
 9     for(int i=0;i<=n;i++)
10         if(G.vexs[i]==u)
11             return i;
12 }
13
14 void prim(AMGRAPH G, char u)
15 {//以名字u为起点,在邻接矩阵表示的图G中,构建最小生成树
16
17     int k=locate(G,u);//寻找u名字在定点表中的下标
18     for(int i=0;i<n;i++)
19         if(i!=k) closedge[i]={u,G.arc[k][j]};//对起点的邻接点初始化
20     closedge[k].lowcost =0;//为0就代表着选过了
21
22     for(int i=1;i<G.vexnum;i++)
23     {//选择其余n-1个点
24
25         int temp=INT-MAX,k;
26         for(int j=0;j<G.vexnum;j++)
27         {
28             if(closedge[j].lowcost<temp&&closedge[j].lowcost!=0)//对于未选的最小权值的点
29                 k=j;
30         }
31
32         char u0=closedge[k].adjvex;//属于U
33         char v0=G.vexs[k];         //属于V-U
34         cout<<u0<<v0;
35
36         closedge[k].lowcost =0;//标记k,代表选了
37
38         for(int j=0;j<G.vexnum;++j){//刷新与k的邻接点,若权值比lowcost小,就刷新
39             if(G.arc[k][j]<closedge[j].lowcost )
40                 closedge[j].lowcost=G.arc[k][j];
41                 closedge[j].adjvex =G.vex[k];
42         }
43     }
44 }

好啦,这算是彻底领悟了,那就看看更简单的克鲁斯卡尔算法;

克鲁斯卡尔

与prim不同,kruskal是加边

与课本不同,这是另一种描述,很像哈夫曼树

克鲁斯卡尔算法:先构造一个只含n个顶点,但边集为空的子图,

        把子图中各个顶点看成各棵树的根节点,之后,

        从边集选权值最小的边,若该边的两个顶点属于不同的树

        就加进子图,把两棵树并成一颗,以此类推,直至森林中只有一棵树。

这个定义本质还是不断让两个连通分量变成一个,知道子图连通。

证明:这这这显而易见啊,不过还是要证的,网上的术语好烦,这里以后补。

那么如何判断一条边连的是不是两个不同的连通分量呢?

如果是同一连通分量,那么加入这条边就一定成环,我们可以判断它成不成环,

或者直接判断属不属于同一个连通分量,答案呼之欲出——并查集

代码:

 1 struct node{
 2     int u,v,w;
 3 }map[maxn];
 4
 5 bool cmp(node a,node b)
 6 {
 7     return a.w <b.w ;
 8 }
 9
10 int find(int x)
11 {
12     return x==p[x]?x:p[x]=find(p[x]);//并查集,一直找掌门,并将中间赋值
13 }
14
15 void kelusikaer (node map[] ,int u)
16 {//对图map图,计算最小生成树的边权和
17     int p[maxn];
18     for(int i=0;i<n;i++)
19         p[i]=i;
20     /*这里输入数据*/
21     sort(map,map+k,cmp);//k为边的数目
22     int ans=0;
23     for(int i=0;i<k;i++)
24     {
25         int x=find(map[i].u );
26         int y=find(map[i].v );
27         if(x!=y){//如果不等,就代表不是同一个连通分量
28             ans+=map[i].w ;
29             p[x]=y;//代表合并了两个连通分量
30         }
31     }
32     cout<<ans<<endl;
33 }

  

    

原文地址:https://www.cnblogs.com/qq2210446939/p/10867459.html

时间: 2024-10-14 07:35:02

最小生成树——普利姆 克鲁斯卡尔的相关文章

最小生成树算法汇总 (普里姆 &amp;&amp; 克鲁斯卡尔与并查集结合)

最小生成树: 今天研究了一下最小生成树,感觉最小生成树算法与最短路算法 相差不大,从Prim 与 Dijskrs算法可以看出 最小生成树即最小权重生成树,主要适用于 N个点之间 构造N-1线路,使N个点之间任意两点之间都可到达, 但是N个点之间 不构成回路,并且这N-1条线路的权重之和最小即 消耗最小. 注意:在构造最小生成树,加入新的节点时,不仅要保证权重最小,首要条件是 不能构成回路. 以图示为例,构造最小生成树 (一)普里姆   以下步骤 (二) 克鲁斯卡尔 最终的最小生成树 和 普里姆一

POJ-2421-Constructing Roads(最小生成树 普利姆)

Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26694   Accepted: 11720 Description There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two

最小生成树普利姆算法

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include<stdlib.h>#include<string.h>#define MAX 20#define INFINITY  0x7ffffffftypedef struct{    int U[MAX][MAX];    char adj[MAX][10];    int vexnum, arcnum;}Graph;typedef struct{    char

最小生成树 - 普利姆算法

#include<cstdlib> #include<cstdio> #include<cstring> #define MAXVEX 100 #define INFINITY 65535 typedef struct { char vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVextexes,numEdges; }MGraph; void CreateGraph(MGraph *G){ int i,j,k,w; prin

(转)最小生成树之普利姆算法、克鲁斯卡尔算法

 最小生成树之prim算法 边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以把边上的权值解释为线路的造价.则最小生成树表示使其造价最小的生成树. 构造网的最小生成树必须解决下面两个问题: 1.尽可能选取权值小的边,但不能构成回路: 2.选取n-1条恰当的边以连通n个顶点: MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集.若(

数据结构(五) 普利姆与克鲁斯卡尔的最小生成树(Swift面向对象版)

上篇博客我们聊了图的物理存储结构邻接矩阵和邻接链表,然后在此基础上给出了图的深度优先搜索和广度优先搜索.本篇博客就在上一篇博客的基础上进行延伸,也是关于图的.今天博客中主要介绍两种算法,都是关于最小生成树的,一种是Prim算法,另一个是Kruskal算法.这两种算法是很经典的,也是图中比较重要的算法了. 今天博客会先聊一聊Prim算法是如何生成最小生成树的,然后给出具体步骤的示例图,最后给出具体的代码实现,并进行测试.当然Kruskal算法也是会给出具体的示例图,然后给出具体的代码和测试用例.当

普利姆算法(最小生成树)

int prim(){ int minid, i, j; double mincost; for(i = 2; i <= n; i ++){ lowcost[i] = map[1][i]; } lowcost[1] = -1; for(i = 2; i <= n; i ++){ mincost = INF; minid = 0; for(j = 2; j <= n; j ++){ if(lowcost[j] < mincost && lowcost[j] >

c/c++ 用普利姆(prim)算法构造最小生成树

c/c++ 用普利姆(prim)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: ? 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路.这时,自然会考虑,如何在最节省经费的前提下建立这个公路网络. ? 每2个城市之间都可以设置一条公路,相应地都要付出一定的经济代价.n个城市之间,最多可以设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少? 普利姆(prim)算法的大致思路: ? 大致思想是:设图G顶点

数据结构之 普利姆算法总结

Agri-Net Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Description Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet connectivity to all farms in the area.