数据结构—— 图:最小生成树问题

Posted 大彤小忆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构—— 图:最小生成树问题相关的知识,希望对你有一定的参考价值。

4. 最小生成树问题

4.1 什么是最小生成树

  一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

  最小生成树(Minimum Spanning Tree): ■ 是一棵树
                       ⋄ 无回路
                       ⋄ |V|个顶点一定有|V|-1条边
                      ■ 是生成树
                       ⋄ 包含全部顶点
                       ⋄ |V|-1条边都在图里
                      ■ 边的权重和最小

  向生成树中任加一条边,都一定构成回路,如下图所示,左上角第一幅图是完全图,其他三幅图均为生成图。

在这里插入图片描述
  结论: 最小生成树存在 ↔ \\leftrightarrow 图连通。

4.2 贪心算法

  贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。

  ■ 什么是 “贪”:每一步都要最好的
  ■ 什么是 “好”:权重最小的边
  ■ 需要约束: ⋄ 只能用图里有的边
         ⋄ 只能正好用掉|V|-1条边
         ⋄ 不能有回路

4.2.1 Prim算法

  普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

  Prim算法,让一棵小树长大。核心思想:贪心思想,找到临近最短的就更新。

  Prim算法的步骤

  • step1:在下图所示的图中,首先选择 v 1 v_{1} v1作为根节点;

在这里插入图片描述

  • step2:寻找与 v 1 v_{1} v1相关的边中权重最小的一条,即 v 1 v_{1} v1 v 4 v_{4} v4之间的边,并将其结点 v 4 v_{4} v4收录进来;

在这里插入图片描述

  • step3:寻找与 v 1 v_{1} v1 v 4 v_{4} v4相关的边中权重最小的一条,即 v 1 v_{1} v1 v 2 v_{2} v2之间的边,并将其结点 v 2 v_{2} v2收录进来;

在这里插入图片描述

  • step4:寻找与 v 1 v_{1} v1 v 4 v_{4} v4 v 2 v_{2} v2相关的边中权重最小的一条,即 v 4 v_{4} v4 v 3 v_{3} v3之间的边,并将其结点 v 3 v_{3} v3收录进来;

在这里插入图片描述

  • step5:寻找与 v 1 v_{1} v1 v 4 v_{4} v4 v 2 v_{2} v2 v 3 v_{3} v3相关的边中权重最小的一条,即 v 4 v_{4} v4 v 7 v_{7} v7之间的边,并将其结点 v 7 v_{7} v7收录进来(不能将 v 2 v_{2} v2 v 4 v_{4} v4 v 1 v_{1} v1 v 3 v_{3} v3之间的边收录进来,因为会构成一个回路);

在这里插入图片描述

  • step6:寻找与 v 1 v_{1} v1 v 4 v_{4} v4 v 2 v_{2} v2 v 3 v_{3} v3 v 7 v_{7} v7相关的边中权重最小的一条,即 v 7 v_{7} v7 v 6 v_{6} v6之间的边,并将其结点 v 6 v_{6} v6收录进来;

在这里插入图片描述

  • step7:寻找与 v 1 v_{1} v1 v 4 v_{4} v4 v 2 v_{2} v2 v 3 v_{3} v3 v 7 v_{7} v7 v 6 v_{6} v6相关的边中权重最小的一条,即 v 7 v_{7} v7 v 5 v_{5} v5之间的边,并将其结点 v 5 v_{5} v5收录进来。

在这里插入图片描述
  Prim算法的代码如下所示。

void Prim ()
{ 
    MST= {s};
    while (1){
        V = 未收录顶点中dist最小者;
        if (这样的V不存在)
            break;
        将V收录进MST: dist[v]= 0 ;
        for ( V的每个邻接点W )
            if ( dist[W]!=0 )
                if (E(V,W)<dist[W]){
                    dist[W]=E(V,W);
                    parent[W]=V;
                }
    }
    if(MST中收的顶点不到|V|)
        Error ("生成树不存在" );
}

  初始化: d i s t [ V ] = E ( s , V ) dist[V]=E_{(s,V)} dist[V]=E(s,V)或正无穷
       parent[s] =-1

  Prim算法的时间复杂度 T = O ( ∣ V ∣ 2 ) T=O(|V|^{2}) T=O(V2),对稠密图合算。

4.2.2 Kruskal算法

  克鲁斯卡尔算法(Kruskal算法)是求连通网的最小生成树的另一种方法。与普里姆算法不同,它的时间复杂度为 O ( e l o g e ) O(eloge) O(eloge)(e为网中的边数),所以,适合于求边稀疏的网的最小生成树 。

  Kruskal算法,将森林合并成树。核心思想:不停地找权重最小的边合在一起,但需要注意的是,不能构成回路。如何找最小权重所在的边,可以靠最小堆来解决;如何判断是否构成回路,可以用并查集看是否属于同一棵树。

  Kruskal算法的步骤