From ea7bbca4bfac7cdf8a43924de9520ee03f60c338 Mon Sep 17 00:00:00 2001 From: yi-ji Date: Sat, 28 Sep 2019 00:21:47 -0500 Subject: [PATCH 1/7] Link-Cut-Tree implementation and unit tests --- include/boost/graph/link_cut_tree.hpp | 279 ++++++++++++++++++++++++++ test/Jamfile.v2 | 1 + test/link_cut_tree_test.cpp | 170 ++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 include/boost/graph/link_cut_tree.hpp create mode 100644 test/link_cut_tree_test.cpp diff --git a/include/boost/graph/link_cut_tree.hpp b/include/boost/graph/link_cut_tree.hpp new file mode 100644 index 000000000..3e73ce884 --- /dev/null +++ b/include/boost/graph/link_cut_tree.hpp @@ -0,0 +1,279 @@ +// +//======================================================================= +// Copyright 2019 +// Author: Yi Ji +// +// 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_LINK_CUT_TREE_HPP +#define BOOST_LINK_CUT_TREE_HPP + +#include +#include + +namespace boost +{ + template + class link_cut_tree + { + public: + inline link_cut_tree(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} + + inline link_cut_tree(const link_cut_tree &c) + : parent(c.parent), left(c.left), right(c.right) {} + + template + inline void make_tree(Element x) + { + make_path(x); + } + + template + inline Element find_root(Element x) + { + return find_tail(expose(x)); + } + + template + inline void link(Element x, Element y) // Element x must be a tree root + { + Element r = expose(x); + r = join(r, r, expose(y)); + put_successor(r, r); + } + + template + inline void cut(Element x) + { + expose(x); + std::pair uv = split(x); + put_successor(x, x); + put_successor(uv.second, uv.second); + } + + template + inline Element lowest_common_ancestor(Element x, Element y) // Elements x and y must have same root + { + expose(x); + return expose(y); + } + + private: + ElementParentMap parent; + ElementChildMap left, right; + + template + inline Element get_parent(Element x) const + { + Element x_parent = get(parent, x); + if (get(left, x_parent) == x || get(right, x_parent) == x) + return x_parent; + return x; // x_parent is actually x_successor when x has no parent + } + + template + inline Element get_successor(Element x) const + { + return get(parent, x); + } + + template + inline void put_successor(Element x, Element x_successor) + { + put(parent, x, x_successor); + } + + template + inline void rotate(const Element x, const ElementChildMap &side) + { + const ElementChildMap &opposite = (&side == &left) ? right : left; + const Element pivot = get(side, x); + const Element x_parent = get_parent(x); + const Element pivot_side = get(opposite, pivot); + + if (x_parent != x) + { + put(parent, pivot, x_parent); + put(get_side(x), x_parent, pivot); + } + else + { + Element x_successor = get_successor(x); + put_successor(pivot, x == x_successor ? pivot : x_successor); + } + + if (pivot_side != pivot) + { + put(side, x, pivot_side); + put(parent, pivot_side, x); + } + else + put(side, x, x); + + put(opposite, pivot, x); + put(parent, x, pivot); + } + + template + inline ElementChildMap& get_side(Element x) + { + Element x_parent = get_parent(x); + if (get(left, x_parent) == x) + return left; + return right; + } + + template + inline void splay(Element x) + { + for (Element x_parent = get_parent(x); x != x_parent; x_parent = get_parent(x)) + { + const Element x_grandparent = get_parent(x_parent); + const ElementChildMap &x_side = get_side(x); + const ElementChildMap &x_parent_side = get_side(x_parent); + + if (x_grandparent == x_parent) + rotate(x_parent, x_side); + else if (&x_side == &x_parent_side) + { + rotate(x_grandparent, x_parent_side); + rotate(x_parent, x_side); + } + else + { + rotate(x_parent, x_side); + rotate(x_grandparent, x_parent_side); + } + } + } + + template + inline Element expose(Element x) + { + Element r = x; + while (true) + { + const Element x_successor = get_successor(find_path(x)); + const std::pair uv = split(x); + if (x != uv.first) + put_successor(uv.first, x); + r = join(r, x, uv.second); + if (x == x_successor) + break; + x = x_successor; + } + put_successor(r, r); + return r; + } + + template + inline void make_path(Element x) + { + put(parent, x, x); + put(right, x, x); + put(left, x, x); + } + + template + inline Element find_path(Element x) + { + splay(x); + return x; + } + + template + inline Element find_tail(Element x) + { + while (get(right, x) != x) + x = get(right, x); + splay(x); + return x; + } + + template + inline Element join(Element u, Element v, Element w) + { + if (u != v) + put(parent, u, v); + if (w != v) + put(parent, w, v); + put(left, v, u); + put(right, v, w); + return v; + } + + template + inline std::pair split(Element x) + { + splay(x); + Element x_left = get(left, x); + Element x_right = get(right, x); + if (x_left != x) + put(parent, x_left, x_left); + if (x_right != x) + put(parent, x_right, x_right); + put(left, x, x); + put(right, x, x); + return std::make_pair<>(x_left, x_right); + } + }; + + + template + class link_cut_tree_with_storage : + public link_cut_tree::value_type, typename property_traits::value_type> > > + { + public: + typedef typename property_traits::key_type Vertex; + typedef typename property_traits::value_type Index; + typedef std::unordered_map IndexMapContainer; + typedef associative_property_map IndexMap; + typedef link_cut_tree LCT; + + link_cut_tree_with_storage(ID id_ = ID()) : + LCT(IndexMap(parent_map), IndexMap(left_map), IndexMap(right_map)), + id(id_) {} + + template + inline void make_tree(Vertex x) + { + const Index x_id = get(id, x); + LCT::make_tree(x_id); + id_to_vertex[x_id] = x; + } + + template + inline Vertex find_root(Vertex x) + { + return id_to_vertex[LCT::find_root(get(id, x))]; + } + + template + inline void link(Vertex x, Vertex y) + { + LCT::link(get(id, x), get(id, y)); + } + + template + inline void cut(Vertex x) + { + LCT::cut(get(id, x)); + } + + template + inline Vertex lowest_common_ancestor(Vertex x, Vertex y) + { + return id_to_vertex[LCT::lowest_common_ancestor(get(id, x), get(id, y))]; + } + + private: + ID id; + std::unordered_map id_to_vertex; + IndexMapContainer parent_map, left_map, right_map; + }; +} + +#endif // BOOST_LINK_CUT_TREE_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b4fcfd4f3..b5f383762 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -112,6 +112,7 @@ alias graph_test_regular : [ run king_ordering.cpp ] [ run matching_test.cpp ] [ run weighted_matching_test.cpp ] + [ run link_cut_tree_test.cpp ] [ run max_flow_test.cpp ] [ run boykov_kolmogorov_max_flow_test.cpp ] [ run cycle_ratio_tests.cpp ../build//boost_graph ../../regex/build//boost_regex : $(CYCLE_RATIO_INPUT_FILE) ] diff --git a/test/link_cut_tree_test.cpp b/test/link_cut_tree_test.cpp new file mode 100644 index 000000000..3557de0df --- /dev/null +++ b/test/link_cut_tree_test.cpp @@ -0,0 +1,170 @@ +//======================================================================= +// Copyright (c) 2019 Yi Ji +// +// 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) +// +//======================================================================= + +#define BOOST_TEST_MODULE link_cut_tree_test + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; + +std::size_t lowest_common_ancestor(std::size_t N, std::size_t u, std::size_t w) +{ + const std::size_t size = std::max(u, w) + 1; + std::vector ancester_of_u(size, false), ancester_of_w(size, false); + while (u > 0 || w > 0) + { + ancester_of_u[u] = ancester_of_w[w] = true; + if (ancester_of_u[w]) + { + return w; + } + if (ancester_of_w[u]) + { + return u; + } + u = u > 0 ? (u - 1) / N : 0; + w = w > 0 ? (w - 1) / N : 0; + } + return 0; +} + +template +void test_link_cut_tree(LinkCutTree lct, const std::vector &elements) +{ + BOOST_FOREACH(const Element &ele, elements) + { + lct.make_tree(ele); + BOOST_CHECK(lct.find_root(ele) == ele); + } + + for (int i = 0; i < elements.size() - 1; ++i) + { + lct.link(elements[i+1], elements[i]); + BOOST_CHECK(lct.find_root(elements[i]) == elements[0]); + BOOST_CHECK(lct.find_root(elements[i+1]) == elements[0]); + BOOST_CHECK(lct.lowest_common_ancestor(elements[i+1], elements[i]) == elements[i]); + } + + BOOST_FOREACH(const Element &ele, elements) + { + lct.cut(ele); + BOOST_CHECK(lct.find_root(ele) == ele); + } + + for (int N = 2; N < 7; ++N) + { + for (unsigned long i = elements.size() - 1; i > 0; --i) + { + unsigned long i_parent = (i - 1) / N; + lct.link(elements[i], elements[i_parent]); + BOOST_CHECK(lct.lowest_common_ancestor(elements[i], elements[i_parent]) == + elements[i_parent]); + std::deque queue; + queue.push_back(i); + while (!queue.empty()) + { + unsigned long idx = queue.front(); + queue.pop_front(); + BOOST_CHECK(lct.find_root(elements[idx]) == elements[i_parent]); + unsigned long idx_child = (idx + 1) * N; + if (idx_child < elements.size()) + { + for (int i = 0; i < N; ++i) + { + queue.push_back(idx_child-i); + } + } + } + } + for (int i = 0; i < elements.size(); ++i) + { + BOOST_CHECK(lct.find_root(elements[i]) == elements[0]); + for (int j = 0; j < elements.size(); ++j) + { + BOOST_CHECK(lct.lowest_common_ancestor(elements[i], elements[j]) == + elements[lowest_common_ancestor(N, i, j)]); + } + } + BOOST_FOREACH(const Element &ele, adaptors::reverse(elements)) + { + lct.cut(ele); + BOOST_CHECK(lct.find_root(ele) == ele); + } + } +} + +BOOST_AUTO_TEST_CASE(link_cut_tree_test1) +{ + typedef associative_property_map< std::map > map_t; + typedef link_cut_tree link_cut_tree_t; + std::vector elements(100); + std::iota(std::begin(elements), std::end(elements), -49); + std::map parent_map, left_map, right_map; + map_t parent(parent_map), left(left_map), right(right_map); + link_cut_tree_t lct(parent, left, right); + test_link_cut_tree(lct, elements); +} + +BOOST_AUTO_TEST_CASE(link_cut_tree_test2) +{ + typedef associative_property_map< std::map > map_t; + typedef associative_property_map< std::unordered_map > unordered_map_t; + typedef link_cut_tree link_cut_tree_t; + std::vector elements(20); + std::iota(std::begin(elements), std::end(elements), 'a'); + std::map parent_map; + std::unordered_map left_map, right_map; + map_t parent(parent_map); + unordered_map_t left(left_map), right(right_map); + link_cut_tree_t lct(parent, left, right); + test_link_cut_tree(lct, elements); +} + +BOOST_AUTO_TEST_CASE(link_cut_tree_with_storage_test1) +{ + typedef typed_identity_property_map map_t; + typedef link_cut_tree_with_storage link_cut_tree_t; + std::vector elements; + std::vector numbers(100); + std::iota(std::begin(numbers), std::end(numbers), -49); + std::transform(std::begin(numbers), + std::end(numbers), + std::back_inserter(elements), + boost::bind(lexical_cast, _1)); + link_cut_tree_t lct; + test_link_cut_tree(lct, elements); +} + +BOOST_AUTO_TEST_CASE(link_cut_tree_with_storage_test2) +{ + typedef associative_property_map< std::map > map_t; + typedef link_cut_tree_with_storage link_cut_tree_t; + std::vector elements; + std::vector numbers(100); + std::map id_map; + map_t id(id_map); + std::iota(std::begin(numbers), std::end(numbers), -49); + BOOST_FOREACH(int i, numbers) + { + std::string i_str = std::to_string(i); + elements.push_back(i_str); + put(id, i_str, i); + } + link_cut_tree_t lct(id); + test_link_cut_tree(lct, elements); +} From abce5b5b535b339dd067a604b01d74388a93f0aa Mon Sep 17 00:00:00 2001 From: yi-ji Date: Sun, 6 Oct 2019 23:20:03 +0900 Subject: [PATCH 2/7] fix stdc++ compatibility issues; parameterize type for id_to_vertex; rename link_cut_tree to link_cut_trees; add docs --- doc/bibliography.html | 5 + doc/link_cut_trees.html | 209 ++++++++++++++++++ .../{link_cut_tree.hpp => link_cut_trees.hpp} | 31 +-- test/Jamfile.v2 | 2 +- ..._tree_test.cpp => link_cut_trees_test.cpp} | 108 +++++---- 5 files changed, 295 insertions(+), 60 deletions(-) create mode 100644 doc/link_cut_trees.html rename include/boost/graph/{link_cut_tree.hpp => link_cut_trees.hpp} (88%) rename test/{link_cut_tree_test.cpp => link_cut_trees_test.cpp} (55%) diff --git a/doc/bibliography.html b/doc/bibliography.html index b541abf62..11a4597f8 100644 --- a/doc/bibliography.html +++ b/doc/bibliography.html @@ -453,6 +453,11 @@

Bibliography

Data Structures for Weighted Matching and Nearest Common Ancestors with Linking
Proceedings of the First Annual ACM-SIAM Symposium on Discrete Algorithms, pp. 434-443, 1990. +

77 +
Robert Endre Tarjan
+Linking and Cutting Trees
+Data Structures and Network Algorithms, ISBN: 978-0-89871-187-5, pp. 59-70, 1983. +
diff --git a/doc/link_cut_trees.html b/doc/link_cut_trees.html new file mode 100644 index 000000000..9d13c232b --- /dev/null +++ b/doc/link_cut_trees.html @@ -0,0 +1,209 @@ + + + + + + + + Boost Link/Cut Trees + + + + C++ Boost
+ +

Link/Cut Trees

+
link_cut_trees<ElementParentMap, ElementChildMap>
+ +

A link/cut-trees data structure + maintains a forest of element nodes subject to dynamic linking and cutting operations. + Rooted trees are encoded in both the ElementParentMap and ElementChildMap + property maps. Splay trees are used internally to represent every link/cut-tree [77]..

+ +

Where Defined

boost/graph/link_cut_trees.hpp + +

Template Parameters

+ + + + + + + + + + + + + +
ElementParentMapMust be a model of ReadWritePropertyMap + and the key and value type the same as the trees' element type.
ElementChildMapDefault the same as ElementParentMap. Also must be a model of ReadWritePropertyMap + and the key and value type the same as the trees' element type.
+ +

Example

+ +

A typical usage pattern for link_cut_trees can be seen in the + dynamic connectivity problem for acyclic graphs. Given two nodes x and y, + they are connected if and only if find_root(x) == find_root(y).

+ +
+  ...
+  link_cut_trees<ElementParentMap, ElementChildMap> lct(p, l, r);
+
+  for (ui = vertices(G).first; ui != vertices(G).second; ++ui)
+    lct.make_tree(*ui);
+  ...
+  while ( !Q.empty() ) {
+    e = Q.front();
+    Q.pop();
+    u = lct.find_root(source(e));
+    v = lct.find_root(target(e));
+    if ( u != v ) {
+      *out++ = e;
+      lct.link(u, v);
+    }
+  }
+  ...
+  for (ui = vertices(G).first; ui != vertices(G).second; ++ui)
+    lct.cut(*ui);
+
+ +

Members

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MemberDescription
link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r)Constructor.
link_cut_trees(const disjoint_sets& c)Copy constructor.
template <class Element>
+ void make_tree(Element x)
Create a singleton tree containing element x.
template <class Element>
+ Element find_root(Element x)
Return the root of the tree containing element x.
template <class Element>
+ void link(Element x, Element y)
Make the tree rooted at element x a subtree of Element y. Element x must be a tree root.
template <class Element>
+ void cut(Element x)
Remove the edge connecting x to its parent and make x a tree root.
template <class Element>
+ Element lowest_common_ancestor(Element x, Element y)
Return the lowest (i.e. nearest) common ancestor of elements x and y. Elements x and y must have the same root.
+ +

Complexity

+ +

The amortized time complexity is O(log n) for link, cut, find_root and lowest_common_ancestor operations, + where n is the number of tree nodes. Operation make_tree is of constant time complexity.

+ +
+
link_cut_trees_with_storage<ID, InverseID>
+ +

This class manages the storage for the parent and children properties + internally. The storage is in boost::unordered_map, which is indexed by element ID, + hence the requirement for the ID and InverseID functors. + +

Template Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescriptionDefault
IDmust be a model of ReadablePropertyMap that + maps elements to values of any type that can be hashed to std::size_t.boost::identity_property_map
InverseIDmust be a model of ReadablePropertyMap that + maps values of property_traits<ID>::value_type to elements.boost::unordered_map
+ +

Members

+ +

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

+
link_cut_trees_with_storage(ID id = ID(), InverseID inverse_id = InverseID())
+ +
+ +

Valid HTML 4.01 Transitional

+ +

Revised + 07 + October, 2019

+ + + + + + + +
Copyright © 2019 + Yi Ji, Peking University (jiy@pku.edu.cn)
+
+ +

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)

+ + \ No newline at end of file diff --git a/include/boost/graph/link_cut_tree.hpp b/include/boost/graph/link_cut_trees.hpp similarity index 88% rename from include/boost/graph/link_cut_tree.hpp rename to include/boost/graph/link_cut_trees.hpp index 3e73ce884..00da5c2c7 100644 --- a/include/boost/graph/link_cut_tree.hpp +++ b/include/boost/graph/link_cut_trees.hpp @@ -8,21 +8,21 @@ // http://www.boost.org/LICENSE_1_0.txt) //======================================================================= // -#ifndef BOOST_LINK_CUT_TREE_HPP -#define BOOST_LINK_CUT_TREE_HPP +#ifndef BOOST_LINK_CUT_TREES_HPP +#define BOOST_LINK_CUT_TREES_HPP #include -#include +#include namespace boost { template - class link_cut_tree + class link_cut_trees { public: - inline link_cut_tree(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} + inline link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} - inline link_cut_tree(const link_cut_tree &c) + inline link_cut_trees(const link_cut_trees &c) : parent(c.parent), left(c.left), right(c.right) {} template @@ -222,20 +222,21 @@ namespace boost }; - template - class link_cut_tree_with_storage : - public link_cut_tree::value_type, typename property_traits::value_type> > > + template ::value_type, typename property_traits::key_type> > + class link_cut_trees_with_storage : + public link_cut_trees::value_type, typename property_traits::value_type> > > { public: typedef typename property_traits::key_type Vertex; typedef typename property_traits::value_type Index; - typedef std::unordered_map IndexMapContainer; + typedef boost::unordered_map IndexMapContainer; typedef associative_property_map IndexMap; - typedef link_cut_tree LCT; + typedef link_cut_trees LCT; - link_cut_tree_with_storage(ID id_ = ID()) : + link_cut_trees_with_storage(ID id_ = ID(), InverseID inverse_id = InverseID()) : LCT(IndexMap(parent_map), IndexMap(left_map), IndexMap(right_map)), - id(id_) {} + id(id_), + id_to_vertex(inverse_id) {} template inline void make_tree(Vertex x) @@ -271,9 +272,9 @@ namespace boost private: ID id; - std::unordered_map id_to_vertex; + InverseID id_to_vertex; IndexMapContainer parent_map, left_map, right_map; }; } -#endif // BOOST_LINK_CUT_TREE_HPP +#endif // BOOST_LINK_CUT_TREES_HPP \ No newline at end of file diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b5f383762..ae1616777 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -112,7 +112,7 @@ alias graph_test_regular : [ run king_ordering.cpp ] [ run matching_test.cpp ] [ run weighted_matching_test.cpp ] - [ run link_cut_tree_test.cpp ] + [ run link_cut_trees_test.cpp ] [ run max_flow_test.cpp ] [ run boykov_kolmogorov_max_flow_test.cpp ] [ run cycle_ratio_tests.cpp ../build//boost_graph ../../regex/build//boost_regex : $(CYCLE_RATIO_INPUT_FILE) ] diff --git a/test/link_cut_tree_test.cpp b/test/link_cut_trees_test.cpp similarity index 55% rename from test/link_cut_tree_test.cpp rename to test/link_cut_trees_test.cpp index 3557de0df..634b9078c 100644 --- a/test/link_cut_tree_test.cpp +++ b/test/link_cut_trees_test.cpp @@ -7,18 +7,16 @@ // //======================================================================= -#define BOOST_TEST_MODULE link_cut_tree_test +#define BOOST_TEST_MODULE link_cut_trees_test -#include +#include #include #include #include #include +#include +#include #include -#include -#include -#include -#include using namespace boost; @@ -44,7 +42,7 @@ std::size_t lowest_common_ancestor(std::size_t N, std::size_t u, std::size_t w) } template -void test_link_cut_tree(LinkCutTree lct, const std::vector &elements) +void test_link_cut_trees(LinkCutTree lct, const std::vector &elements) { BOOST_FOREACH(const Element &ele, elements) { @@ -52,7 +50,7 @@ void test_link_cut_tree(LinkCutTree lct, const std::vector &elements) BOOST_CHECK(lct.find_root(ele) == ele); } - for (int i = 0; i < elements.size() - 1; ++i) + for (std::size_t i = 0; i < elements.size() - 1; ++i) { lct.link(elements[i+1], elements[i]); BOOST_CHECK(lct.find_root(elements[i]) == elements[0]); @@ -66,35 +64,35 @@ void test_link_cut_tree(LinkCutTree lct, const std::vector &elements) BOOST_CHECK(lct.find_root(ele) == ele); } - for (int N = 2; N < 7; ++N) + for (std::size_t N = 2; N < 7; ++N) { - for (unsigned long i = elements.size() - 1; i > 0; --i) + for (std::size_t i = elements.size() - 1; i > 0; --i) { - unsigned long i_parent = (i - 1) / N; + std::size_t i_parent = (i - 1) / N; lct.link(elements[i], elements[i_parent]); BOOST_CHECK(lct.lowest_common_ancestor(elements[i], elements[i_parent]) == elements[i_parent]); - std::deque queue; + std::deque queue; queue.push_back(i); while (!queue.empty()) { - unsigned long idx = queue.front(); + std::size_t idx = queue.front(); queue.pop_front(); BOOST_CHECK(lct.find_root(elements[idx]) == elements[i_parent]); - unsigned long idx_child = (idx + 1) * N; + std::size_t idx_child = (idx + 1) * N; if (idx_child < elements.size()) { - for (int i = 0; i < N; ++i) + for (std::size_t i = 0; i < N; ++i) { queue.push_back(idx_child-i); } } } } - for (int i = 0; i < elements.size(); ++i) + for (std::size_t i = 0; i < elements.size(); ++i) { BOOST_CHECK(lct.find_root(elements[i]) == elements[0]); - for (int j = 0; j < elements.size(); ++j) + for (std::size_t j = 0; j < elements.size(); ++j) { BOOST_CHECK(lct.lowest_common_ancestor(elements[i], elements[j]) == elements[lowest_common_ancestor(N, i, j)]); @@ -108,63 +106,85 @@ void test_link_cut_tree(LinkCutTree lct, const std::vector &elements) } } -BOOST_AUTO_TEST_CASE(link_cut_tree_test1) +BOOST_AUTO_TEST_CASE(link_cut_trees_test1) { typedef associative_property_map< std::map > map_t; - typedef link_cut_tree link_cut_tree_t; + typedef link_cut_trees link_cut_trees_t; std::vector elements(100); - std::iota(std::begin(elements), std::end(elements), -49); + boost::range::iota(elements, -49); std::map parent_map, left_map, right_map; map_t parent(parent_map), left(left_map), right(right_map); - link_cut_tree_t lct(parent, left, right); - test_link_cut_tree(lct, elements); + link_cut_trees_t lct(parent, left, right); + test_link_cut_trees(lct, elements); } -BOOST_AUTO_TEST_CASE(link_cut_tree_test2) +BOOST_AUTO_TEST_CASE(link_cut_trees_test2) { typedef associative_property_map< std::map > map_t; - typedef associative_property_map< std::unordered_map > unordered_map_t; - typedef link_cut_tree link_cut_tree_t; + typedef associative_property_map< boost::unordered_map > unordered_map_t; + typedef link_cut_trees link_cut_trees_t; std::vector elements(20); - std::iota(std::begin(elements), std::end(elements), 'a'); + boost::range::iota(elements, 'a'); std::map parent_map; - std::unordered_map left_map, right_map; + boost::unordered_map left_map, right_map; map_t parent(parent_map); unordered_map_t left(left_map), right(right_map); - link_cut_tree_t lct(parent, left, right); - test_link_cut_tree(lct, elements); + link_cut_trees_t lct(parent, left, right); + test_link_cut_trees(lct, elements); } -BOOST_AUTO_TEST_CASE(link_cut_tree_with_storage_test1) +BOOST_AUTO_TEST_CASE(link_cut_trees_test3) { typedef typed_identity_property_map map_t; - typedef link_cut_tree_with_storage link_cut_tree_t; + typedef link_cut_trees_with_storage link_cut_trees_t; std::vector elements; std::vector numbers(100); - std::iota(std::begin(numbers), std::end(numbers), -49); - std::transform(std::begin(numbers), - std::end(numbers), - std::back_inserter(elements), - boost::bind(lexical_cast, _1)); - link_cut_tree_t lct; - test_link_cut_tree(lct, elements); + boost::range::iota(numbers, -49); + std::transform(numbers.begin(), + numbers.end(), + std::back_inserter(elements), + boost::bind(lexical_cast, _1)); + link_cut_trees_t lct; + test_link_cut_trees(lct, elements); } -BOOST_AUTO_TEST_CASE(link_cut_tree_with_storage_test2) +BOOST_AUTO_TEST_CASE(link_cut_trees_test4) { typedef associative_property_map< std::map > map_t; - typedef link_cut_tree_with_storage link_cut_tree_t; + typedef link_cut_trees_with_storage link_cut_trees_t; std::vector elements; std::vector numbers(100); std::map id_map; map_t id(id_map); - std::iota(std::begin(numbers), std::end(numbers), -49); + boost::range::iota(numbers, -49); BOOST_FOREACH(int i, numbers) { - std::string i_str = std::to_string(i); + std::string i_str = lexical_cast(i); elements.push_back(i_str); put(id, i_str, i); } - link_cut_tree_t lct(id); - test_link_cut_tree(lct, elements); + link_cut_trees_t lct(id); + test_link_cut_trees(lct, elements); } + +BOOST_AUTO_TEST_CASE(link_cut_trees_test5) +{ + typedef associative_property_map< std::map > id_map_t; + typedef vector_property_map inverse_id_map_t; + typedef link_cut_trees_with_storage link_cut_trees_t; + std::vector elements; + std::vector numbers(100); + std::map id_map; + id_map_t id(id_map); + inverse_id_map_t inverse_id; + boost::range::iota(numbers, -49); + BOOST_FOREACH(int i, numbers) + { + std::string i_str = lexical_cast(i); + elements.push_back(i_str); + put(id, i_str, i+49); + put(inverse_id, i+49, i_str); + } + link_cut_trees_t lct(id, inverse_id); + test_link_cut_trees(lct, elements); +} \ No newline at end of file From 3aa8cd3459173f4e96560e69dd570c425052537c Mon Sep 17 00:00:00 2001 From: yi-ji Date: Tue, 8 Oct 2019 23:58:07 +0900 Subject: [PATCH 3/7] add missing headers --- test/link_cut_trees_test.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/link_cut_trees_test.cpp b/test/link_cut_trees_test.cpp index 634b9078c..855207483 100644 --- a/test/link_cut_trees_test.cpp +++ b/test/link_cut_trees_test.cpp @@ -16,13 +16,17 @@ #include #include #include +#include #include +#include +#include +#include using namespace boost; std::size_t lowest_common_ancestor(std::size_t N, std::size_t u, std::size_t w) { - const std::size_t size = std::max(u, w) + 1; + const std::size_t size = std::max(u, w) + 1; std::vector ancester_of_u(size, false), ancester_of_w(size, false); while (u > 0 || w > 0) { @@ -140,10 +144,7 @@ BOOST_AUTO_TEST_CASE(link_cut_trees_test3) std::vector elements; std::vector numbers(100); boost::range::iota(numbers, -49); - std::transform(numbers.begin(), - numbers.end(), - std::back_inserter(elements), - boost::bind(lexical_cast, _1)); + boost::range::transform(numbers, std::back_inserter(elements), boost::bind(lexical_cast, _1)); link_cut_trees_t lct; test_link_cut_trees(lct, elements); } From 95ee00c07bd35258f733ba4c8257869bbf120c26 Mon Sep 17 00:00:00 2001 From: yi-ji Date: Sat, 20 Jun 2020 16:33:48 +0900 Subject: [PATCH 4/7] Parameterize the index map type, some code style & doc fixes --- doc/link_cut_trees.html | 8 ++++---- include/boost/graph/link_cut_trees.hpp | 9 +++++---- test/link_cut_trees_test.cpp | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/doc/link_cut_trees.html b/doc/link_cut_trees.html index 9d13c232b..0c9564301 100644 --- a/doc/link_cut_trees.html +++ b/doc/link_cut_trees.html @@ -52,7 +52,7 @@

Example

   ...
-  link_cut_trees<ElementParentMap, ElementChildMap> lct(p, l, r);
+  link_cut_trees<ElementParentMap, ElementChildMap> lct(parent_map, left_child_map, right_child_map);
 
   for (ui = vertices(G).first; ui != vertices(G).second; ++ui)
     lct.make_tree(*ui);
@@ -88,7 +88,7 @@ 

Members

- link_cut_trees(const disjoint_sets& c) + link_cut_trees(const link_cut_trees& c) Copy constructor. @@ -188,8 +188,8 @@

Members

height="31" width="88">

Revised - 07 - October, 2019

+ + June, 2020

diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp index 00da5c2c7..217d3ec6e 100644 --- a/include/boost/graph/link_cut_trees.hpp +++ b/include/boost/graph/link_cut_trees.hpp @@ -22,7 +22,7 @@ namespace boost public: inline link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} - inline link_cut_trees(const link_cut_trees &c) + inline link_cut_trees(const link_cut_trees &c) : parent(c.parent), left(c.left), right(c.right) {} template @@ -222,14 +222,15 @@ namespace boost }; - template ::value_type, typename property_traits::key_type> > + template ::value_type, typename property_traits::key_type>, + class IndexMapContainer = boost::unordered_map::value_type, typename property_traits::value_type> > class link_cut_trees_with_storage : - public link_cut_trees::value_type, typename property_traits::value_type> > > + public link_cut_trees > { public: typedef typename property_traits::key_type Vertex; typedef typename property_traits::value_type Index; - typedef boost::unordered_map IndexMapContainer; typedef associative_property_map IndexMap; typedef link_cut_trees LCT; diff --git a/test/link_cut_trees_test.cpp b/test/link_cut_trees_test.cpp index 855207483..9c4abed70 100644 --- a/test/link_cut_trees_test.cpp +++ b/test/link_cut_trees_test.cpp @@ -188,4 +188,26 @@ BOOST_AUTO_TEST_CASE(link_cut_trees_test5) } link_cut_trees_t lct(id, inverse_id); test_link_cut_trees(lct, elements); +} + +BOOST_AUTO_TEST_CASE(link_cut_trees_test6) +{ + typedef associative_property_map< std::map > id_map_t; + typedef vector_property_map inverse_id_map_t; + typedef link_cut_trees_with_storage > link_cut_trees_t; + std::vector elements; + std::vector numbers(100); + std::map id_map; + id_map_t id(id_map); + inverse_id_map_t inverse_id; + boost::range::iota(numbers, -49); + BOOST_FOREACH(int i, numbers) + { + std::string i_str = lexical_cast(i); + elements.push_back(i_str); + put(id, i_str, i+49); + put(inverse_id, i+49, i_str); + } + link_cut_trees_t lct(id, inverse_id); + test_link_cut_trees(lct, elements); } \ No newline at end of file From 2c9585396f68f733421ee837f67d47986b007763 Mon Sep 17 00:00:00 2001 From: yi-ji Date: Tue, 23 Jun 2020 17:52:25 +0900 Subject: [PATCH 5/7] Add same root assertion for `link` and `lowest_common_ancestor`; Move typename outside of template --- include/boost/graph/link_cut_trees.hpp | 6 ++++-- test/link_cut_trees_test.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp index 217d3ec6e..7d7a4e6ae 100644 --- a/include/boost/graph/link_cut_trees.hpp +++ b/include/boost/graph/link_cut_trees.hpp @@ -38,8 +38,9 @@ namespace boost } template - inline void link(Element x, Element y) // Element x must be a tree root + inline void link(Element x, Element y) { + BOOST_ASSERT(find_root(x) == x); // Element x must be a tree root Element r = expose(x); r = join(r, r, expose(y)); put_successor(r, r); @@ -55,8 +56,9 @@ namespace boost } template - inline Element lowest_common_ancestor(Element x, Element y) // Elements x and y must have same root + inline Element lowest_common_ancestor(Element x, Element y) { + BOOST_ASSERT(find_root(x) == find_root(y)); // Elements x and y must have same root expose(x); return expose(y); } diff --git a/test/link_cut_trees_test.cpp b/test/link_cut_trees_test.cpp index 9c4abed70..b9279a833 100644 --- a/test/link_cut_trees_test.cpp +++ b/test/link_cut_trees_test.cpp @@ -194,7 +194,8 @@ BOOST_AUTO_TEST_CASE(link_cut_trees_test6) { typedef associative_property_map< std::map > id_map_t; typedef vector_property_map inverse_id_map_t; - typedef link_cut_trees_with_storage > link_cut_trees_t; + typedef std::map index_map_container_t; + typedef link_cut_trees_with_storage link_cut_trees_t; std::vector elements; std::vector numbers(100); std::map id_map; From 372d53fe216d61e4eec85bfd63511fe341e2191d Mon Sep 17 00:00:00 2001 From: yiji Date: Wed, 24 Mar 2021 17:17:14 +0900 Subject: [PATCH 6/7] remove `inline` function modifiers --- include/boost/graph/link_cut_trees.hpp | 58 ++++++++++++-------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/include/boost/graph/link_cut_trees.hpp b/include/boost/graph/link_cut_trees.hpp index 7d7a4e6ae..d39444c08 100644 --- a/include/boost/graph/link_cut_trees.hpp +++ b/include/boost/graph/link_cut_trees.hpp @@ -20,25 +20,27 @@ namespace boost class link_cut_trees { public: - inline link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} + link_cut_trees(ElementParentMap p, ElementChildMap l, ElementChildMap r) : parent(p), left(l), right(r) {} - inline link_cut_trees(const link_cut_trees &c) + link_cut_trees(const link_cut_trees &c) : parent(c.parent), left(c.left), right(c.right) {} template - inline void make_tree(Element x) + void make_tree(Element x) { - make_path(x); + put(parent, x, x); + put(right, x, x); + put(left, x, x); } template - inline Element find_root(Element x) + Element find_root(Element x) { return find_tail(expose(x)); } template - inline void link(Element x, Element y) + void link(Element x, Element y) { BOOST_ASSERT(find_root(x) == x); // Element x must be a tree root Element r = expose(x); @@ -47,7 +49,7 @@ namespace boost } template - inline void cut(Element x) + void cut(Element x) { expose(x); std::pair uv = split(x); @@ -56,7 +58,7 @@ namespace boost } template - inline Element lowest_common_ancestor(Element x, Element y) + Element lowest_common_ancestor(Element x, Element y) { BOOST_ASSERT(find_root(x) == find_root(y)); // Elements x and y must have same root expose(x); @@ -68,7 +70,7 @@ namespace boost ElementChildMap left, right; template - inline Element get_parent(Element x) const + Element get_parent(Element x) const { Element x_parent = get(parent, x); if (get(left, x_parent) == x || get(right, x_parent) == x) @@ -77,19 +79,19 @@ namespace boost } template - inline Element get_successor(Element x) const + Element get_successor(Element x) const { return get(parent, x); } template - inline void put_successor(Element x, Element x_successor) + void put_successor(Element x, Element x_successor) { put(parent, x, x_successor); } template - inline void rotate(const Element x, const ElementChildMap &side) + void rotate(const Element x, const ElementChildMap &side) { const ElementChildMap &opposite = (&side == &left) ? right : left; const Element pivot = get(side, x); @@ -120,7 +122,7 @@ namespace boost } template - inline ElementChildMap& get_side(Element x) + ElementChildMap& get_side(Element x) { Element x_parent = get_parent(x); if (get(left, x_parent) == x) @@ -129,7 +131,7 @@ namespace boost } template - inline void splay(Element x) + void splay(Element x) { for (Element x_parent = get_parent(x); x != x_parent; x_parent = get_parent(x)) { @@ -153,7 +155,7 @@ namespace boost } template - inline Element expose(Element x) + Element expose(Element x) { Element r = x; while (true) @@ -172,22 +174,14 @@ namespace boost } template - inline void make_path(Element x) - { - put(parent, x, x); - put(right, x, x); - put(left, x, x); - } - - template - inline Element find_path(Element x) + Element find_path(Element x) { splay(x); return x; } template - inline Element find_tail(Element x) + Element find_tail(Element x) { while (get(right, x) != x) x = get(right, x); @@ -196,7 +190,7 @@ namespace boost } template - inline Element join(Element u, Element v, Element w) + Element join(Element u, Element v, Element w) { if (u != v) put(parent, u, v); @@ -208,7 +202,7 @@ namespace boost } template - inline std::pair split(Element x) + std::pair split(Element x) { splay(x); Element x_left = get(left, x); @@ -242,7 +236,7 @@ namespace boost id_to_vertex(inverse_id) {} template - inline void make_tree(Vertex x) + void make_tree(Vertex x) { const Index x_id = get(id, x); LCT::make_tree(x_id); @@ -250,25 +244,25 @@ namespace boost } template - inline Vertex find_root(Vertex x) + Vertex find_root(Vertex x) { return id_to_vertex[LCT::find_root(get(id, x))]; } template - inline void link(Vertex x, Vertex y) + void link(Vertex x, Vertex y) { LCT::link(get(id, x), get(id, y)); } template - inline void cut(Vertex x) + void cut(Vertex x) { LCT::cut(get(id, x)); } template - inline Vertex lowest_common_ancestor(Vertex x, Vertex y) + Vertex lowest_common_ancestor(Vertex x, Vertex y) { return id_to_vertex[LCT::lowest_common_ancestor(get(id, x), get(id, y))]; } From 749c9a44b97121be5fb3859a1adb67b873ee69ee Mon Sep 17 00:00:00 2001 From: yi-ji Date: Sun, 13 Jun 2021 00:41:15 +0900 Subject: [PATCH 7/7] Empty commit to trigger a build