zoukankan      html  css  js  c++  java
  • 最小生成树-Kruskal算法

    Prim算法贪心选择不同,Kruskal算法采取每次选择权值最小的边的方法,这样,在不构成环且最后能够连接完所有边它们的权重和一定是最小的。

    和之前Prim算法的图一样,便于区别二者。

    Kruskal既然是选择最小的边,那么就先找一个最小的出来,是1-6(10)

    然后继续找出剩下的边中最小一条边,是3-4(12)

    继续找一条最小的出来,2-7(14)

    在来,为了区别已经选择的点,我把点的颜色也做个标记,有颜色的表示为已经加入生成树的点

    喔,忘了把最小的边2-3(16)选了

    现在图变成了这样,(依旧难看).......

    还没找完,继续找最小的边..是7-4(18)

    你会发现我把这条边用蓝色标记了,是不是我为了好玩??当然不是了,这条边是有问题的。

    这条边虽然是上一次选择完后剩下的边中最短的,但是,它的左右两个点已经是最小生成树的结点了,而把这条边加入后,构成了右边的一个大大的环。这样显然不是最小生成树了.

    再复习下,生成树:一个连通图的生成树,是一个极小连通子图,其中包含图的所有结点,和构成一棵数的(n-1)条边。如果在一棵生成树的两个结点上添加任意一边,必定构成一个环。

    最小生成树:图的所有生成树中所有边的权值和最小的那个生成树

    所以,有环的连生成树都不是了,怎么会是最小生成树。。

    所以,这条边7-4(18)丢掉丢掉,重新选择。选择5-4(22)

    好像图中所有的点都有颜色了,但是还没完全好,因为它们还不是一条绳上的蚂蚱,

    继续选,最短的是6-5(25)

    继续选 ,唉,不对,选了24发现又有环了,选28也构成环了。。。。在仔细一看,最小生成树已经生成了

    这个就是Kruskal算法的流程。那么接下来说说代码。

     1 struct node
     2 {
     3     int u;
     4     int v;
     5     int w;//边的权重
     6 }Edge;
     7 void Kruskal(Graph g)//无向图g采用邻接矩阵
     8 {
     9     Edge E[MAXN];//存放图中所有的边
    10     int vest[MAXN];//辅助数组,存放连通的点的编号
    11     k = 0;
    12     for(int i = 0;i < g.n;i++)
    13     {
    14         for(int j = 0; j <= i ;j++)
    15         {
    16             if(g.egdes[i][j] != 0 &&g.egdes[i][j]!= INF)
    17             {//说明i到j边
    18                 E.[k].u = i;
    19                 E.[k].v = j;
    20                 E.[k].w = g.edges[i][j];
    21                 k++;
    22             }
    23         }
    24     }//以上将所有的边都加入到E数组中,肯定是不重复的
    25     Sort(E);//对边集数组进行排序。
    26     Init(vest);//将辅助数组初始化,即每个结点一开始都没有加入到最小生成树中,所以它们各自为一个阵营
    27     j = 0;
    28     Count = 0 ;
    29     while(Count < g.n)//已经加入生成树的结点数小于图的结点数,表明生成树没生成完,
    30     {
    31         u1 = E[j].u;
    32         v1 = E[j].v;//选出最短的边
    33         s1 = vest[u1];
    34         s2 = vest[v1];//得到两个所在集合的编号,
    35         if(s1 != s2)//如果它们编号不等,,说明它们还每加入到同一个生成树中,
    36         {
    37             //这里可以打印它们的编号 。。。
    38             k++;
    39             for(int i = 0; i< g.n;i++)
    40             {
    41                 if(vest[i]==s2)
    42                 {
    43                     vest[i] = s1;//把属于s2的那个点加入到s1所在点集合中
    44                 }//即合并到一起去,表明它们在一个生成树中了,这样就和
    45                 //其他没有加入到的点区分开了
    46             }
    47         }
    48         j++;//继续找下一条边
    49     }
    50 }
    View Code

    这个代码没有写全,因为这样用的次数少的可怜了。都用它的升级版了

    首先,针对排序的问题,排序随你选,接下来就是判断是否构成环的问题了。这里用并查集轻易的实现

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<vector>
     6 #include<stack>
     7 #include<map>
     8 #include<set>
     9 #include<list>
    10 #include<queue>
    11 #include<string>
    12 #include<algorithm>
    13 #include<iomanip>
    14 using namespace std;
    15 const int maxn = 100;
    16 int par[maxn];
    17 int rank[maxn];
    18 int n;
    19 int m;
    20 
    21 struct node
    22 {
    23     int Start;
    24     int End;
    25     int weight;
    26 }edges[maxn];
    27 
    28 int cmp(node a,node b)//按权值从小到大排序
    29 {
    30     return a.weight < b.weight;
    31 }
    32 
    33 void Init(int n)//par初始化为自己
    34 {
    35     for(int i = 1;i <= n;i++)
    36     {
    37         par[i] = i;
    38     }
    39 }
    40 int  Find(int x)//找出父亲结点
    41 {
    42     if(x != par[x])return x =Find(par[x]);
    43     return x;
    44 }
    45 
    46 int  Kruskal()
    47 {
    48     int sum = 0;
    49     for(int i = 1;i <= m ;i++)
    50     {
    51         int a = Find(edges[i].Start);
    52         int b = Find(edges[i].End);
    53         if(a != b)//父亲结点不同
    54         {
    55             sum += edges[i].weight;//一般求最小生成树的长度,这里就没去掉
    56             par[a] = b;//合并集合
    57         }
    58     }
    59     return sum;
    60 }
    61 
    62 int main()
    63 {
    64     
    65     cin>>n>>m;
    66     Init(n);
    67     for(int i = 1;i <= m;i++)
    68     {
    69         cin>>edges[i].Start>>edges[i].End>>edges[i].weight;
    70     }
    71     sort(edges+1,edges+1+n,cmp); 
    72     cout<<Kruskal();
    73     return 0;
    74 }
    View Code

    传送门:NetWork  不会的点这里

     

  • 相关阅读:
    PHP 数据库连接
    php函数
    php数组基础
    Jquery元素追加和删除
    jquery获取父,同,子级元素
    深入理解css中position属性及z-index属性
    php运算符
    MySQL主键与索引的区别和联系
    php 常量
    20150408--Sphinx+邮件激活-02
  • 原文地址:https://www.cnblogs.com/ygsworld/p/11256505.html
Copyright © 2011-2022 走看看