zoukankan      html  css  js  c++  java
  • 最小生成树算法(未完成)

    关于图的几个概念定义:

    • 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
    • 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
    • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
    • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
    • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。 

    两个最小生成树(Minimum Spanning Tree, MST)算法完全等价。

    如果输入邻接矩阵,一般用Prim。

    如果输入边表,一般用Kruskal。

    1.Kruskal算法

    时间复杂度和把所有边排序的复杂度一样,为O(m log m)O(mlogm)。

    如果只需要求最小生成树的最大边的权值,可以在O(m)O(m)的时间内求出。
    (二分,每次丢掉一半)

    此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
    1. 把图中的所有边按代价从小到大排序; 
    2. 把图中的n个顶点看成独立的n棵树组成的森林; 
    3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
    4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

     代码:

     1 struct node
     2 {
     3     //x,y表示边的左右端点,value表示边的值
     4     int x,y,value;
     5 }no[205];
     6 //n表示点数,m表示边数
     7 int n,m;
     8 //fa表示树上的顶点
     9 int fa[105];
    10 //初始化
    11 void init()
    12 {
    13     //开始时顶点是自己
    14     for(int i=0;i<=n;i++)
    15     {
    16         fa[i]=i;
    17     }
    18 }
    19 //查找顶点
    20 int find(int x)
    21 {
    22     //如果找到顶点,则返回
    23     if(fa[x]==x)
    24     {
    25         return x;
    26     }
    27     //递归查找x的上一个点的上一个点,同时压缩路径
    28     return fa[x]=find(fa[x]);
    29 }
    30 //排序,按边的值从小到大
    31 bool cmp(node a,node b)
    32 {
    33     return a.value<b.value;
    34 }
    35 //最小生成树Kruskal算法
    36 int kruskal()
    37 {
    38     //对边进行排序
    39     sort(no+1,no+m+1,cmp);
    40     //初始化数据
    41     init();
    42     //ans表示最小生成树的权值和,cnt表示已经连接的边数
    43     int ans=0,cnt=0;
    44     //遍历所有边
    45     for(int i=1;i<=m;i++)
    46     {
    47         //如果连接了n-1个边,则表示已经连接所有的点了
    48         if(cnt==n-1)
    49         {
    50             break;
    51         }
    52         //记录当前边的两边端点的顶点
    53         int fx=find(no[i].x),fy=find(no[i].y);
    54         //如果他们不相等则进行操作
    55         if(fx!=fy)
    56         {
    57             //将fx的顶点变成fy,相当于把fx这个树的顶点挂在fy这个树顶的下面
    58             fa[fx]=fy;
    59             //生成树的边数加1
    60             cnt++;
    61             //加上这个边的权值
    62             ans+=no[i].value;
    63         }
    64     }
    65     //如果连了n-1条边则表示有最小生成树
    66     if(cnt==n-1)
    67     {
    68         return ans;
    69     }
    70     else
    71     {
    72         return -1;
    73     }
    74 }

    2.Prim算法

    可以使用堆优化,类似Dijkstra,但是使用堆优化,效率未必提高。

    对于完全图的情况:

    不使用堆优化,时间复杂度为O(n^2)O(n2);

    使用堆优化,时间复杂度为O(n^2 log n)O(n2logn)。

    所以说对于以邻接矩阵输入的图,没必要去用Kruskal。

    设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。

    初始状态:

    设置2个数据结构

    lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说明以i为终点的边的最小权值=0,也就是表示i点加入了MST

    mst[i]:表示对应lowcost[i]的起点,即说明边<mst[i],i>是MST的一条边,当mst[i]=0表示起点i加入MST

    我们假设V1是起始点,进行初始化(*代表无限大,即无通路):

    lowcost[2]=6,lowcost[3]=1,lowcost[4]=5,lowcost[5]=*,lowcost[6]=*

    mst[2]=1,mst[3]=1,mst[4]=1,mst[5]=1,mst[6]=1,(所有点默认起点是V1)

    明显看出,以V3为终点的边的权值最小=1,所以边<mst[3],3>=1加入MST

    此时,因为点V3的加入,需要更新lowcost数组和mst数组:

    lowcost[2]=5,lowcost[3]=0,lowcost[4]=5,lowcost[5]=6,lowcost[6]=4

    mst[2]=3,mst[3]=0,mst[4]=1,mst[5]=3,mst[6]=3

    明显看出,以V6为终点的边的权值最小=4,所以边<mst[6],6>=4加入MST

    此时,因为点V6的加入,需要更新lowcost数组和mst数组:

    lowcost[2]=5,lowcost[3]=0,lowcost[4]=2,lowcost[5]=6,lowcost[6]=0

    mst[2]=3,mst[3]=0,mst[4]=6,mst[5]=3,mst[6]=0

    明显看出,以V4为终点的边的权值最小=2,所以边<mst[4],4>=4加入MST

    此时,因为点V4的加入,需要更新lowcost数组和mst数组:

    lowcost[2]=5,lowcost[3]=0,lowcost[4]=0,lowcost[5]=6,lowcost[6]=0

    mst[2]=3,mst[3]=0,mst[4]=0,mst[5]=3,mst[6]=0

    明显看出,以V2为终点的边的权值最小=5,所以边<mst[2],2>=5加入MST

    此时,因为点V2的加入,需要更新lowcost数组和mst数组:

    lowcost[2]=0,lowcost[3]=0,lowcost[4]=0,lowcost[5]=3,lowcost[6]=0

    mst[2]=0,mst[3]=0,mst[4]=0,mst[5]=2,mst[6]=0

    很明显,以V5为终点的边的权值最小=3,所以边<mst[5],5>=3加入MST

    lowcost[2]=0,lowcost[3]=0,lowcost[4]=0,lowcost[5]=0,lowcost[6]=0

    mst[2]=0,mst[3]=0,mst[4]=0,mst[5]=0,mst[6]=0

    至此,MST构建成功,如图所示:

    代码:

     1 //存放地图
     2 int ma[100][100];
     3 //lowcost存放到起点的距离
     4 //mst表示i指向的点,
     5 int lowcost[100],mst[100];
     6 //n表示点数
     7 int n;
     8 //初始化地图
     9 void init()
    10 {
    11     for(int i=0;i<=n;i++)
    12     {
    13         for(int j=0;j<=n;j++)
    14         {
    15             //本地到本地的权值为0,到其他点的权值为无穷
    16             if(i==j)
    17             {
    18                 ma[i][j]=0;
    19             }
    20             else
    21             {
    22                 ma[i][j]=INF;
    23             }
    24         }
    25     }
    26 }
    27 //u表示起点
    28 int prim(int u)
    29 {
    30     //ans表示树上所有边的权值和
    31     int ans=0;
    32     //初始化lowcost,mst
    33     for(int i=1;i<=n;i++)
    34     {
    35         //将所有指向起点的边录入lowcost中
    36         lowcost[i] = ma[u][i];
    37         //所有的点都指向起点
    38         mst[i]=u;
    39     }
    40     //起点已在树上,所以为-1
    41     mst[u] = -1;
    42     //遍历其他n-1个点
    43     for(int i=1;i<n;i++)
    44     {
    45         //当前最小权值为INF
    46         int mi=INF;
    47         //当前最小值指向的点是-1
    48         int v=-1;
    49         //遍历所有的点
    50         for(int j=1;j<=n;j++)
    51         {
    52             //如果该点不在树上,且该点到起点的距离小于当前最小权值
    53             if(mst[j]!=-1&&lowcost[j]<mi)
    54             {
    55                 //记录这个较小的点和权值
    56                 v=j;
    57                 mi=lowcost[j];
    58             }
    59         }
    60         //v!=-1表示在所有与树连接的最小权值
    61         if(v!=-1)
    62         {
    63             //将这个点标记
    64             mst[v]=-1;
    65             //累计这个边的权值
    66             ans+=lowcost[v];
    67             //遍所有的点
    68             for(int j=1;j<=n;j++)
    69             {
    70                 //如果j不在树上且j到树上的距离比j到v的距离大
    71                 //更新这个数据,并将mst[j]指向v
    72                 if(mst[j]!=-1&&lowcost[j]>ma[v][j])
    73                 {
    74                     lowcost[j]=ma[v][j];
    75                     mst[j]=v;
    76                 }
    77             }
    78         }
    79     }
    80     //返回树上所有边的权值和
    81     return ans;
    82 }
  • 相关阅读:
    与众不同 windows phone (50)
    与众不同 windows phone (49)
    重新想象 Windows 8.1 Store Apps (93)
    重新想象 Windows 8.1 Store Apps 系列文章索引
    重新想象 Windows 8.1 Store Apps (92)
    重新想象 Windows 8.1 Store Apps (91)
    重新想象 Windows 8.1 Store Apps (90)
    重新想象 Windows 8.1 Store Apps (89)
    重新想象 Windows 8.1 Store Apps (88)
    重新想象 Windows 8.1 Store Apps (87)
  • 原文地址:https://www.cnblogs.com/mzchuan/p/11720280.html
Copyright © 2011-2022 走看看