diff --git a/doc/challenge.html b/doc/challenge.html index 072ba134e..89a34dec6 100644 --- a/doc/challenge.html +++ b/doc/challenge.html @@ -37,7 +37,7 @@

Boost Graph Library Challenge and To-Do Items

mutable_queue.hpp, fibonacci_heap.hpp. Somehow merge implementation with Dietmer's heaps and queues. -
  • disjoint_sets
  • +
  • disjoint_sets (see )
  • Construct a set of planar graph algorithms.
  • diff --git a/doc/disjoint_sets.html b/doc/disjoint_sets.html new file mode 100644 index 000000000..4bff1bf98 --- /dev/null +++ b/doc/disjoint_sets.html @@ -0,0 +1,309 @@ + + + + + + + + Boost Disjoint Sets + + + + C++ Boost
    + +

    Disjoint + Sets

    +
    +disjoint_sets<Rank, Parent, FindCompress>
    +
    + +

    This is a class that provides disjoint set operations with union by + rank and path compression. A disjoint-sets data structure + maintains a collection S = {S1, S2, ..., + Sk} of disjoint sets. Each set is identified by a + representative which is some member of of the set. Sets are + represented by rooted trees which are encoded in the Parent + property map. Two heuristics: "union by rank" and "path compression" are + used to speed up the operations [1, 2].

    + +

    Where Defined

    boost/pending/disjoint_sets.hpp + +

    Template Parameters

    + + + + + + + + + + + + + + + + + + + +
    Rankmust be a model of ReadWritePropertyMap + with an integer value type and a key type equal to the set's element + type.
    Parentmust be a model of ReadWritePropertyMap + and the key and value type the same as the set's element type.
    FindCompressshould be one of the find representative and path compress function + objects.
    + +

    Example

    + +

    A typical usage pattern for disjoint_sets can be seen in the + kruskal_minimum_spanning_tree() + algorithm. In this example, we call link() instead of + union_set() because u and v were obtained from + find_set() and therefore are already the representatives for their + sets.

    +
    +  ...
    +  disjoint_sets<Rank, Parent, FindCompress> dsets(rank, p);
    +  
    +  for (ui  = vertices(G).first; ui != vertices(G).second; ++ui)
    +    dsets.make_set(*ui);
    +  ...
    +  while ( !Q.empty() ) {
    +    e = Q.front();
    +    Q.pop();
    +    u = dsets.find_set(source(e));
    +    v = dsets.find_set(target(e));
    +    if ( u != v ) {
    +      *out++ = e;
    +      dsets.link(u, v);
    +    }
    +  }
    +
    + +

    Members

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    MemberDescription
    disjoint_sets(Rank r, Parent p)Constructor.
    disjoint_sets(const disjoint_sets& x)Copy constructor.
    template <class Element>
    + void make_set(Element x)
    Creates a singleton set containing Element x.
    template <class Element>
    + void link(Element x, Element y)
    Union the two sets represented by element x and + y.
    template <class Element>
    + void union_set(Element x, Element y)
    Union the two sets that contain elements x and + y. This is equivalent to + link(find_set(x),find_set(y)).
    template <class Element>
    + Element find_set(Element x)
    Return the representative for the set containing element + x.
    template <class ElementIterator>
    + std::size_t count_sets(ElementIterator first, ElementIterator + last)
    Returns the number of disjoint sets.
    template <class ElementIterator>
    + void compress_sets(ElementIterator first, ElementIterator + last)
    Flatten the parents tree so that the parent of every element is its + representative.
    + +

    Complexity

    + +

    The time complexity is O(m alpha(m,n)), where alpha is the + inverse Ackermann's function, m is the number of disjoint-set + operations (make_set(), find_set(), and link() + and n is the number of elements. The alpha function grows + very slowly, much more slowly than the log function.

    + +

    See Also

    incremental_connected_components() +
    +
    +disjoint_sets_with_storage<ID,InverseID,FindCompress>
    +
    + +

    This class manages the storage for the rank and parent properties + internally. The storage is in arrays, which are indexed by element ID, + hence the requirement for the ID and InverseID functors. + The rank and parent properties are initialized during construction so the + each element is in a set by itself (so it is not necessary to initialize + objects of this class with the + initialize_incremental_components() function). This class is + especially useful when computing the (dynamic) connected components of an + edge_list graph which does not provide a place to store vertex + properties.

    + +

    Template Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterDescriptionDefault
    IDmust be a model of ReadablePropertyMap that + maps elements to integers between zero 0 and N, the total number of + elements in the sets.boost::identity_property_map
    InverseIDmust be a model of ReadablePropertyMap that + maps integers to elements.boost::identity_property_map
    FindCompressshould be one of the find representative and path compress function + objects.representative_with_full_path_compression
    + +

    Members

    + +

    This class has all of the members in disjoint_sets as well as + the following members.

    +
    +disjoint_sets_with_storage(size_type n = 0,
    +                           ID id = ID(),
    +                           InverseID inv = InverseID())
    +
    Constructor. +
    +template <class ElementIterator>
    +void disjoint_sets_with_storage::
    +  normalize_sets(ElementIterator first, ElementIterator last)
    +
    This rearranges the representatives such that the representative of +each set is the element with the smallest ID.
    + Postcondition: v >= parent[v]
    + Precondition: the disjoint sets structure must be compressed.
    +
    + +

    +
    +representative_with_path_halving<Parent>
    +
    + +

    This is a functor which finds the representative vertex for the same + component as the element x. While traversing up the representative + tree, the functor also applies the path halving technique to shorten the + height of the tree.

    +
    +Element operator()(Parent p, Element x)
    +
    +
    + +


    +
    +representative_with_full_path_compression<Parent>
    +
    + +

    This is a functor which finds the representative element for the set + that element x belongs to.

    +
    +Element operator()(Parent p, Element x)
    +
    + +


    +
    + +

    Valid HTML 4.01 Transitional

    + +

    Revised + 01 + December, 2006

    + + + + + + + +
    Copyright © 2000Jeremy Siek, Univ.of + Notre Dame (jsiek@lsc.nd.edu)
    + Lie-Quan Lee, + Univ.of Notre Dame (llee1@lsc.nd.edu)
    + Andrew Lumsdaine, Univ.of + Notre Dame (lums@lsc.nd.edu)
    + +

    Distributed under the Boost Software License, Version 1.0. (See + accompanying file LICENSE_1_0.txt or + copy at http://www.boost.org/LICENSE_1_0.txt)

    + + diff --git a/doc/disjoint_sets_biblio.html b/doc/disjoint_sets_biblio.html new file mode 100644 index 000000000..54fc44a0d --- /dev/null +++ b/doc/disjoint_sets_biblio.html @@ -0,0 +1,59 @@ + + + + + + + + Boost Utility Library: Bibliography + + + + C++ Boost
    + +

    Bibliography

    + +
    +
    1
    + +
    R. E. Tarjan.
    + Data Structures and Network Algorithms.
    + Society for Industrial and Applied Mathematics, 1983.
    + +
     
    + +
    2
    + +
    T. Cormen, C. Leiserson, and R. Rivest.
    + Introduction to Algorithms.
    + McGraw-Hill, 1990.
    +

    +
    + +

    Valid HTML 4.01 Transitional

    + +

    Revised + 01 + December, 2006

    + + + + + + + +
    Copyright © 2000Jeremy Siek, Univ.of + Notre Dame (jsiek@lsc.nd.edu)
    + +

    Distributed under the Boost Software License, Version 1.0. (See + accompanying file LICENSE_1_0.txt or + copy at http://www.boost.org/LICENSE_1_0.txt)

    + + diff --git a/doc/index.html b/doc/index.html index b4e8b650a..61bc71b15 100644 --- a/doc/index.html +++ b/doc/index.html @@ -287,7 +287,6 @@

    Data Structures

    The edge_list class is an adaptor that takes any kind of edge iterator and implements an Edge List Graph. -

    diff --git a/include/boost/pending/detail/disjoint_sets.hpp b/include/boost/pending/detail/disjoint_sets.hpp new file mode 100644 index 000000000..6d1ec3ac7 --- /dev/null +++ b/include/boost/pending/detail/disjoint_sets.hpp @@ -0,0 +1,88 @@ +// (C) Copyright Jeremy Siek 2004 +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_DETAIL_DISJOINT_SETS_HPP +#define BOOST_DETAIL_DISJOINT_SETS_HPP + +namespace boost { + +namespace detail { + +template +Vertex +find_representative_with_path_halving(ParentPA p, Vertex v) +{ + Vertex parent = get(p, v); + Vertex grandparent = get(p, parent); + while (parent != grandparent) { + put(p, v, grandparent); + v = grandparent; + parent = get(p, v); + grandparent = get(p, parent); + } + return parent; +} + +template +Vertex +find_representative_with_full_compression(ParentPA parent, Vertex v) +{ + Vertex old = v; + Vertex ancestor = get(parent, v); + while (ancestor != v) { + v = ancestor; + ancestor = get(parent, v); + } + v = get(parent, old); + while (ancestor != v) { + put(parent, old, ancestor); + old = v; + v = get(parent, old); + } + return ancestor; +} + +/* the postcondition of link sets is: + component_representative(i) == component_representative(j) + */ +template +inline void +link_sets(ParentPA p, RankPA rank, Vertex i, Vertex j, + ComponentRepresentative comp_rep) +{ + i = comp_rep(p, i); + j = comp_rep(p, j); + if (i == j) return; + if (get(rank, i) > get(rank, j)) + put(p, j, i); + else { + put(p, i, j); + if (get(rank, i) == get(rank, j)) + put(rank, j, get(rank, j) + 1); + } +} + +// normalize components has the following postcondidition: +// i >= p[i] +// that is, the representative is the node with the smallest index in its class +// as its precondition it it assumes that the node container is compressed + +template +inline void +normalize_node(ParentPA p, Vertex i) +{ + if (i > get(p,i) || get(p, get(p,i)) != get(p,i)) + put(p,i, get(p, get(p,i))); + else { + put(p, get(p,i), i); + put(p, i, i); + } +} + + } // namespace detail +} // namespace boost + +#endif // BOOST_DETAIL_DISJOINT_SETS_HPP diff --git a/include/boost/pending/disjoint_sets.hpp b/include/boost/pending/disjoint_sets.hpp new file mode 100644 index 000000000..e64bc2b71 --- /dev/null +++ b/include/boost/pending/disjoint_sets.hpp @@ -0,0 +1,220 @@ +// +//======================================================================= +// Copyright 1997, 1998, 1999, 2000 University of Notre Dame. +// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +//======================================================================= +// +#ifndef BOOST_DISJOINT_SETS_HPP +#define BOOST_DISJOINT_SETS_HPP + +#include +#include +#include + +namespace boost { + + struct find_with_path_halving { + template + Vertex operator()(ParentPA p, Vertex v) { + return detail::find_representative_with_path_halving(p, v); + } + }; + + struct find_with_full_path_compression { + template + Vertex operator()(ParentPA p, Vertex v){ + return detail::find_representative_with_full_compression(p, v); + } + }; + + // This is a generalized functor to provide disjoint sets operations + // with "union by rank" and "path compression". A disjoint-set data + // structure maintains a collection S={S1, S2, ..., Sk} of disjoint + // sets. Each set is identified by a representative, which is some + // member of of the set. Sets are represented by rooted trees. Two + // heuristics: "union by rank" and "path compression" are used to + // speed up the operations. + + // Disjoint Set requires two vertex properties for internal use. A + // RankPA and a ParentPA. The RankPA must map Vertex to some Integral type + // (preferably the size_type associated with Vertex). The ParentPA + // must map Vertex to Vertex. + template + class disjoint_sets { + typedef disjoint_sets self; + + inline disjoint_sets() {} + public: + inline disjoint_sets(RankPA r, ParentPA p) + : rank(r), parent(p) {} + + inline disjoint_sets(const self& c) + : rank(c.rank), parent(c.parent) {} + + // Make Set -- Create a singleton set containing vertex x + template + inline void make_set(Element x) + { + put(parent, x, x); + typedef typename property_traits::value_type R; + put(rank, x, R()); + } + + // Link - union the two sets represented by vertex x and y + template + inline void link(Element x, Element y) + { + detail::link_sets(parent, rank, x, y, rep); + } + + // Union-Set - union the two sets containing vertex x and y + template + inline void union_set(Element x, Element y) + { + link(find_set(x), find_set(y)); + } + + // Find-Set - returns the Element representative of the set + // containing Element x and applies path compression. + template + inline Element find_set(Element x) + { + return rep(parent, x); + } + + template + inline std::size_t count_sets(ElementIterator first, ElementIterator last) + { + std::size_t count = 0; + for ( ; first != last; ++first) + if (get(parent, *first) == *first) + ++count; + return count; + } + + template + inline void normalize_sets(ElementIterator first, ElementIterator last) + { + for (; first != last; ++first) + detail::normalize_node(parent, *first); + } + + template + inline void compress_sets(ElementIterator first, ElementIterator last) + { + for (; first != last; ++first) + detail::find_representative_with_full_compression(parent, *first); + } + protected: + RankPA rank; + ParentPA parent; + FindCompress rep; + }; + + + + + template + class disjoint_sets_with_storage + { + typedef typename property_traits::value_type Index; + typedef std::vector ParentContainer; + typedef std::vector RankContainer; + public: + typedef typename ParentContainer::size_type size_type; + + disjoint_sets_with_storage(size_type n = 0, + ID id_ = ID(), + InverseID inv = InverseID()) + : id(id_), id_to_vertex(inv), rank(n, 0), parent(n) + { + for (Index i = 0; i < n; ++i) + parent[i] = i; + } + // note this is not normally needed + template + inline void + make_set(Element x) { + parent[x] = x; + rank[x] = 0; + } + template + inline void + link(Element x, Element y) + { + extend_sets(x,y); + detail::link_sets(&parent[0], &rank[0], + get(id,x), get(id,y), rep); + } + template + inline void + union_set(Element x, Element y) { + Element rx = find_set(x); + Element ry = find_set(y); + link(rx, ry); + } + template + inline Element find_set(Element x) { + return id_to_vertex[rep(&parent[0], get(id,x))]; + } + + template + inline std::size_t count_sets(ElementIterator first, ElementIterator last) + { + std::size_t count = 0; + for ( ; first != last; ++first) + if (parent[*first] == *first) + ++count; + return count; + } + + template + inline void normalize_sets(ElementIterator first, ElementIterator last) + { + for (; first != last; ++first) + detail::normalize_node(&parent[0], *first); + } + + template + inline void compress_sets(ElementIterator first, ElementIterator last) + { + for (; first != last; ++first) + detail::find_representative_with_full_compression(&parent[0], + *first); + } + + const ParentContainer& parents() { return parent; } + + protected: + + template + inline void + extend_sets(Element x, Element y) + { + Index needed = get(id,x) > get(id,y) ? get(id,x) + 1 : get(id,y) + 1; + if (needed > parent.size()) { + rank.insert(rank.end(), needed - rank.size(), 0); + for (Index k = parent.size(); k < needed; ++k) + parent.push_back(k); + } + } + + ID id; + InverseID id_to_vertex; + RankContainer rank; + ParentContainer parent; + FindCompress rep; + }; + +} // namespace boost + +#endif // BOOST_DISJOINT_SETS_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e0c07cf25..25f058bf9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -48,6 +48,7 @@ alias graph_test_regular : [ compile dijkstra_cc.cpp ] [ run dijkstra_heap_performance.cpp : 10000 ] [ run dijkstra_no_color_map_compare.cpp : 10000 ] + [ run disjoint_set_test.cpp ] [ run dominator_tree_test.cpp ] # Unused and deprecated. diff --git a/test/disjoint_set_test.cpp b/test/disjoint_set_test.cpp new file mode 100644 index 000000000..cd588396d --- /dev/null +++ b/test/disjoint_set_test.cpp @@ -0,0 +1,76 @@ +// (C) Copyright Jeremy Siek 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +template +struct test_disjoint_set { + static void do_test() + { + // The following tests are pretty lame, just a basic sanity check. + // Industrial strength tests still need to be written. + +#if !defined(__MWERKS__) || __MWERKS__ > 0x3003 + std::size_t elts[] +#else + std::size_t elts[4] +#endif + = { 0, 1, 2, 3 }; + + const int N = sizeof(elts)/sizeof(*elts); + + DisjointSet ds(N); + + ds.make_set(elts[0]); + ds.make_set(elts[1]); + ds.make_set(elts[2]); + ds.make_set(elts[3]); + + BOOST_CHECK(ds.find_set(0) != ds.find_set(1)); + BOOST_CHECK(ds.find_set(0) != ds.find_set(2)); + BOOST_CHECK(ds.find_set(0) != ds.find_set(3)); + BOOST_CHECK(ds.find_set(1) != ds.find_set(2)); + BOOST_CHECK(ds.find_set(1) != ds.find_set(3)); + BOOST_CHECK(ds.find_set(2) != ds.find_set(3)); + + + ds.union_set(0, 1); + ds.union_set(2, 3); + BOOST_CHECK(ds.find_set(0) != ds.find_set(3)); + int a = ds.find_set(0); + BOOST_CHECK(a == ds.find_set(1)); + int b = ds.find_set(2); + BOOST_CHECK(b == ds.find_set(3)); + + ds.link(a, b); + BOOST_CHECK(ds.find_set(a) == ds.find_set(b)); + BOOST_CHECK(1 == ds.count_sets(elts, elts + N)); + + ds.normalize_sets(elts, elts + N); + ds.compress_sets(elts, elts + N); + BOOST_CHECK(1 == ds.count_sets(elts, elts + N)); + } +}; + +int +test_main(int, char*[]) +{ + using namespace boost; + { + typedef + disjoint_sets_with_storage ds_type; + test_disjoint_set::do_test(); + } + { + typedef + disjoint_sets_with_storage ds_type; + test_disjoint_set::do_test(); + } + return boost::exit_success; +}