• 最小生成树总结


    1. 基本原理:

    贪心法;通用的算法都是采用这种贪心策略,它在每一个步骤中都形成最小生成树的一条边,算法维护一个变的集合A:保持以下的循环不变式:在每一次循环迭代之前,A是某个最小生成树的一个子集;

    2. 基本模式

    GENERIC-MST(G,w)
        A = Q
        while A does not form a spanning tree
            find an edge (u,v) that is safe for A
            A = AU{(u,v)}
        return A

    3. 问题的关键:

    寻找安全边(使A = A{(u,v)}仍然是某一个最小生成树子集的边(u,v))。

    4. 寻找安全边的规则:

    4.1 基本概念

             割:无向图G=(V,E)的一个割(S,V-S)是对V的一个划分。

             边通过割:当无向图G的一条边一个端点属于S,而另一个端点属于V-S时,称该边通过割(S,V-S)

             割不妨害边集A如果一个边的集合A中没有边通过该割。

             轻边(light edge)如果某条边的权值是通过一个割的所有边中最小的,则称该边位通过这个割的一条轻边。另外还有满足某一性质的轻边

    4.2 添加安全边的定理准则

             G=(V,E)是一个无向图且在边E上定义了加权函数值;AG的某个最小生成树的子集。设割(S,V-S)是任意一个不妨害边集A的割,且边(u,v)是通过该割的一条轻边。则该边对集合A来说是安全的。

             理解:

    1)边集A的顶点一定全在SV-S;

             2)包含A的最小生成树可能有多棵;

             3)集合A始终是无回路的;

             4)在算法执行的任一时刻,图GA=(V,A)是一个森林,GA的每一个连通分支都是一棵树;

             5)算法Generic-MST(G,w)每循环一次确定一条最小生成树的边,共执行|V|-1次;初始时,A=GA|V|棵树,每次迭代过程均减少一颗树,当森林只包含一棵树时,算法终止;

             6)该定理可以简述为以下MST性质:假设G=(V,E)是一个连通图网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中uU,vV-U,则必存在一棵包含边(u,v)的最小生成树。

    5. 分类:

    依据通用算法中安全边确定规则细节的不同,分了两种算法:KruskalPrim算法

    5.1 Kruskal算法

    原理:最小生成树子集合A是一个森林,加入集合A中的安全边总是图中连接两个不同联通分支的最小权边;Kruskal算法也是一种贪心算法:算法的每一步中添加到森林中的边的权值都是尽可能小的;

    算法:采用了一种不相交集合数据结构,以维护几个互不相交的元素集合:

    MST-KRUSKAL(G,W)
    
    //初始化:A为空,并建立|V|棵树,每棵树包含图中一顶点
        A =for each vertex v∈G.V
            MAKE-SET(v)
            
        sort the edge(u,v)∈G.E in nondecreasing order by weight w
        
        for edge(u,v)∈G.E, taken in nondecreasing order by weight
            if FIND-SET(u) ≠ FIND-SET(v)
                A = A∪{(u,v)}

        算法时间复杂度:O(ElgE)也可以写成O(ElgV)

    5.2 Prim算法

             原理:集合A仅形成单棵树,添加入集合A的安全边总是连接该树与一个不在树中的顶点的最小权边。因为每次添加到树中的边都是使树的权尽可能小的边,因此,上述策略也是“贪心的”。

             算法:

             相关数据结构:基于key域(权值w)的最小优先队列Q(存储不在树中所有的顶点);key[V]:所有与树A某一顶点相连的边的最小权值;π[V]:各顶点的双亲结点;

    MST-PRIM(G,w,r)
        
    //初始化
        for each u∈G.V
            key[u] = ∞
            π[u] = NIL
        key[r] = 0 //根结点初始化为0,成为第一个被处理的顶点
        Q = V[G]
        
    //依次确定添于树A的下一个结点u,即与A相连的不在A的最小权边
        while Q ≠ ∅
                u = EXTRACT-MIN(Q)//找出与通过割(V-Q,Q)的一条轻边相关联的顶点u∈Q(第一次例外)
                for each v∈Adj[u]//u加入A后更新A邻接点的key值
                    if v∈Q and w(u,v)<key[v]
                        π[v] = u
                        key[v] = w(u,v)

        说明:

        1)算法执行过程中,GENERIC-MST的结合A隐含地满足:A={(v,π[v]):vV-{r}-Q};当算法终止时,Q是空的,G的最小生成树A={(v,π[v]):vV-{r}}

             2)基本原理都知道,实现的关键之一是如何保存已经找到得到最小生成树的子集A,本算法巧妙的利用每个顶点的双亲结点得出A;

             3)算法实现的关键之二是利用一个队列记录从A到剩余顶点具有最小代价的边,即割(V(A),V-A)的轻边;

             时间复杂度:

             算法的初始化部分复杂度为O(V);剩下的while循环执行|V|次,循环体内有两个操作,依次是提取轻边和修改邻接点key值。这样复杂度就取决于优先队列Q是如何实现(数组、二叉队列及Fibonacci队列)及图的数据结构,根据其不同,可分为如下部分:

    Minimum edge weight data structure

    Time complexity (total)

    Searching, adjacency matrix

    O(V2)

    binary heap, adjacency list

    O((V+E)logV) = O(ElogV)

    Fibonacci heap, adjacency list

    O(E+VlogV)

     参考文献:《算法导论》、严蔚敏《数据结构》

  • 相关阅读:
    Linux下多进程编程消息队列
    Linux下多线程编程之——线程专有数据
    Linux下多线程编程之——线程互斥
    Linux下多线程编程之——线程竞争
    Linux下多线程编程之——线程取消
    Linux下多线程编程之——线程分离
    Linux下多线程编程之——多线程委托模型
    Postman 提交测试的时候提示 Bad Request
    Confluence 6 其他页面操作和页面大小
    Confluence 6 页面的组织和移动概述
  • 原文地址:https://www.cnblogs.com/lyfruit/p/3077269.html
Copyright © 2020-2023  润新知