boost disjoint set 实例

前言

最近,需要用C++实现一个简单的disjoint set,查了一下STL,貌似没有已经做好的库,但是boost有。于是我决定测试一下。

测试系统是Ubuntu 18.04 LTS,gcc 7.5.0,boost 1.65.01。

Boost 的disjoint set documentation在
https://www.boost.org/doc/libs/1_72_0/libs/disjoint_sets/disjoint_sets.html

测试样例来自于这个教程
https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/

部分代码结构来自于这个SO提问
https://stackoverflow.com/questions/4134703/understanding-boostdisjoint-sets

经过

样例简单描述是这样的。我们有一个graph,所有edge都有一个weight,edge列表已经根据weight进行了升序排序,我们的目的是需要从这个graph中找到minimum spanning tree(MST)。所用的方法是Kruskal’s Minimum Spanning Tree Algorithm。graph的具体定义和算法描述都在教程中,这里复现一下教程的原图。
教程graph原图
图 1. 教程graph原图。vertex上的数字是index。edge上的数字是weight而 不是 index。版权归原作者所有。

算法原理就是利用disjoint set。从最小weight的edge开始处理,对于一个新的edge E,找出E的两个vertex在disjoint set D中对应的子集。如果两个vertex在相同子集S内,那么向当前的MST中加入E之后将在S内形成一个circle,此时放弃向MST添加E。如果两个vertex在不同的子集中,那么将这两个子集union成新的子集,更新disjoint set D,并向MST中添加E。MST中最多可存在nv-1个edge,其中nv是原始vertex数。

经过Kruskal’s Minimum Spanning Tree Algorithm处理后,所得到的MST如下图所示。
教程MST结果
图 2. 教程中MST的结果。版权归原作者所有。

根据上述问题的设定,我整了一个简单的代码,其中disjoint set部分是boost实现的。

//
// Created by yaoyu on 3/19/20.
//

#include <map>
#include <iostream>
#include <vector>

#include <boost/pending/disjoint_sets.hpp>

template <typename vertexIndexT, typename weightT>
class Edge
{
public:
    // Default constructor.
    Edge() = default;

    // Constructor with vertex indices.
    Edge(const vertexIndexT& v0, const vertexIndexT& v1, const weightT& w) {
        mV0 = v0;
        mV1 = v1;
        mW  = w;
    }

    // Copy constructor.
    Edge(const Edge<vertexIndexT, weightT>& other) {
        this->mV0 = other.mV0;
        this->mV1 = other.mV1;
        this->mW  = other.mW;
    }

    ~Edge() = default;

    // Overload operator =.
    Edge<vertexIndexT, weightT>& operator = (const Edge<vertexIndexT, weightT>& other) {
        if ( this != &other ) {
            this->mV0 = other.mV0;
            this->mV1 = other.mV1;
            this->mW  = other.mW;
        }

        return *this;
    }

    // Overload the stream operator <<.
    friend std::ostream& operator << (std::ostream& out, const Edge<vertexIndexT, weightT>& e) {
        out << "Edge( "
            << e.mW << ", "
            << e.mV0 << ", "
            << e.mV1 << " )";

        return out;
    }

public:
    vertexIndexT mV0;
    vertexIndexT mV1;

    weightT mW;
};

/**
 * This function makes a graph that is described at
 * https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/
 *
 * @tparam vT The type of the vertex index.
 * @tparam wT The type of the edge weight.
 * @param v The vertices.
 * @param e The edges.
 */
template < typename vT, typename wT >
static void make_default_graph( std::vector<vT>& v, std::vector<Edge<vT, wT>>& e ) {
    // Create the vertices.
    for (int i = 0; i < 9; i++) {
        v.push_back(static_cast<vT>(i));
    }

    // Create the edges.
    e.push_back( Edge<vT, wT>( static_cast<vT>(7), static_cast<vT>(6), static_cast<wT>(1.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(8), static_cast<vT>(2), static_cast<wT>(2.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(6), static_cast<vT>(5), static_cast<wT>(2.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(0), static_cast<vT>(1), static_cast<wT>(4.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(2), static_cast<vT>(5), static_cast<wT>(4.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(8), static_cast<vT>(6), static_cast<wT>(6.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(2), static_cast<vT>(3), static_cast<wT>(7.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(7), static_cast<vT>(8), static_cast<wT>(7.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(0), static_cast<vT>(7), static_cast<wT>(8.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(1), static_cast<vT>(2), static_cast<wT>(8.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(3), static_cast<vT>(4), static_cast<wT>(9.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(5), static_cast<vT>(4), static_cast<wT>(10.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(1), static_cast<vT>(7), static_cast<wT>(11.0) ) );
    e.push_back( Edge<vT, wT>( static_cast<vT>(3), static_cast<vT>(5), static_cast<wT>(14.0) ) );
}

template <typename vT, typename wT>
static void display_graph(const std::vector<vT>& vertices, const std::vector<Edge<vT, wT>>& edges) {
    // List the vertices.
    std::cout << "Vertices: " << std::endl;
    for ( const vT& v : vertices ) {
        std::cout << v << ", ";
    }
    std::cout << std::endl << std::endl;

    // List the edges.
    std::cout << "Edges: " << std::endl;
    for ( const Edge<vT, wT>& e : edges ) {
        std::cout << e << std::endl;
    }
}

int main(int argc, char* argv[])
{
    std::cout << "Hello, TryDisjointSet! " << std::endl;

    // The original vertices and edges.
    std::vector<int> vertices;
    typedef Edge<int, double> Edge_t;
    std::vector<Edge_t> edgesOri;

    // The default
    make_default_graph(vertices, edgesOri);
    std::cout << "========== The initial graph. ========== " << std::endl;
    display_graph(vertices, edgesOri);

    // The maps.
    typedef std::map<int, std::size_t> Rank_t;
    typedef std::map<int, int> Parent_t;
    typedef boost::associative_property_map<Rank_t> PropMapRank_t;
    typedef boost::associative_property_map<Parent_t> PropMapParent_t;

    Rank_t          mapRank;
    Parent_t        mapParent;
    PropMapRank_t   propMapRank(mapRank);
    PropMapParent_t propMapParent(mapParent);

    // The disjoint set.
    boost::disjoint_sets<PropMapRank_t, PropMapParent_t> djs( propMapRank, propMapParent );

    // Make a disjoint set with all the vertices as sub-set containing single element.
    for ( const int& v : vertices ) {
        djs.make_set( v );
    }

    std::cout << "djs.count_sets() = " << djs.count_sets( vertices.begin(), vertices.end() ) << std::endl;
    std::cout << std::endl;

    // Make the MST.
    std::cout << "========== Start making the MST. ==========" << std::endl;
    size_t nDJSEdges = 0;
    std::vector<Edge_t> edgesMST; // Stores the edges of the MST.

    for ( const Edge_t& e : edgesOri ) {
        if ( nDJSEdges == vertices.size() - 1 ) {
            std::cout << "Number of edges in the MST reaches the maximum." << std::endl;
            break;
        }

        std::cout << "Process " << e << ": " << std::endl;

        // Get the sub-sets contain the two vertices of the current edge.
        auto u = djs.find_set(e.mV0);
        auto v = djs.find_set(e.mV1);

        // Check if it makes a circle in the MST.
        if ( u != v) {
            // Not a circle.
            djs.link(u, v);
            nDJSEdges++;
            edgesMST.push_back(e);
            std::cout << "Add new sub set." << std::endl;
        } else {
            // A circle will be made if we contain this edge into the MST.
            std::cout << "The two vertices of the edge are in the same sub-set " << u << ". " << std::endl;
        }
    }

    std::cout << "djs.count_sets() = " << djs.count_sets( vertices.begin(), vertices.end() ) << std::endl;
    std::cout << std::endl;

    std::cout << "========== The MST. ==========" << std::endl;

    display_graph(vertices, edgesMST);

    return 0;
}

使用boost最闹心的地方就是没人告诉你这个库是只要头文件就行了,还是要link特定的boost library。目前这个disjoint set的测试结果是不需要link特定的boost library。

运行结果如下:

Hello, TryDisjointSet! 
========== The initial graph. ========== 
Vertices: 
0, 1, 2, 3, 4, 5, 6, 7, 8, 

Edges: 
Edge( 1, 7, 6 )
Edge( 2, 8, 2 )
Edge( 2, 6, 5 )
Edge( 4, 0, 1 )
Edge( 4, 2, 5 )
Edge( 6, 8, 6 )
Edge( 7, 2, 3 )
Edge( 7, 7, 8 )
Edge( 8, 0, 7 )
Edge( 8, 1, 2 )
Edge( 9, 3, 4 )
Edge( 10, 5, 4 )
Edge( 11, 1, 7 )
Edge( 14, 3, 5 )
djs.count_sets() = 9

========== Start making the MST. ==========
Process Edge( 1, 7, 6 ): 
Add new sub set.
Process Edge( 2, 8, 2 ): 
Add new sub set.
Process Edge( 2, 6, 5 ): 
Add new sub set.
Process Edge( 4, 0, 1 ): 
Add new sub set.
Process Edge( 4, 2, 5 ): 
Add new sub set.
Process Edge( 6, 8, 6 ): 
The two vertices of the edge are in the same sub-set 6. 
Process Edge( 7, 2, 3 ): 
Add new sub set.
Process Edge( 7, 7, 8 ): 
The two vertices of the edge are in the same sub-set 6. 
Process Edge( 8, 0, 7 ): 
Add new sub set.
Process Edge( 8, 1, 2 ): 
The two vertices of the edge are in the same sub-set 6. 
Process Edge( 9, 3, 4 ): 
Add new sub set.
Number of edges in the MST reaches the maximum.
djs.count_sets() = 1

========== The MST. ==========
Vertices: 
0, 1, 2, 3, 4, 5, 6, 7, 8, 

Edges: 
Edge( 1, 7, 6 )
Edge( 2, 8, 2 )
Edge( 2, 6, 5 )
Edge( 4, 0, 1 )
Edge( 4, 2, 5 )
Edge( 7, 2, 3 )
Edge( 8, 0, 7 )
Edge( 9, 3, 4 )

后记

还不知道效率怎么样,后面将会在现实条件下测试这个设计。

发布了77 篇原创文章 · 获赞 36 · 访问量 14万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览