理解 boost::disjoint_sets

发布于 2024-10-01 12:43:29 字数 363 浏览 1 评论 0 原文

我需要使用 boost::disjoint_sets,但是 文档 我不清楚。有人可以解释一下每个模板参数的含义,或者给出一个用于创建 disjoint_sets 的小示例代码吗?

根据请求,我使用 disjoint_sets 来实现 Tarjan 的离线最不常见祖先算法,即 -值类型应为 vertex_descriptor。

I need to use boost::disjoint_sets, but the documentation is unclear to me. Can someone please explain what each template parameter means, and perhaps give a small example code for creating a disjoint_sets?

As per the request, I am using disjoint_sets to implement Tarjan's off-line least common ancestors algorithm, i.e - the value type should be vertex_descriptor.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

心舞飞扬 2024-10-08 12:43:29

我从文档中可以理解的是:

不相交的需要将等级和父级(在林树中)与每个元素相关联。例如,由于您可能希望使用任何类型的数据,因此您并不总是希望对父级使用映射:对于整数,数组就足够了。您还需要每个元素的排名(并集查找所需的排名)。

您需要两个“属性”:

  • 一个将整数与每个元素关联(第一个模板参数),将
  • 一个元素与另一个元素关联(第二个模板参数),父级

在一个示例中:

std::vector<int>  rank (100);
std::vector<int>  parent (100);
boost::disjoint_sets<int*,int*> ds(&rank[0], &parent[0]);

使用数组 < code>&rank[0], &parent[0] 到模板中的类型是 int*

对于更复杂的示例(使用地图),您可以查看 Ugo 的答案。

您只需为算法提供两个结构来存储他需要的数据(排名/父级)。

What I can understand from the documentation :

Disjoint need to associate a rank and a parent (in the forest tree) to each element. Since you might want to work with any kind of data you may,for example, not always want to use a map for the parent: with integer an array is sufficient. You also need a rank foe each element (the rank needed for the union-find).

You'll need two "properties" :

  • one to associate an integer to each element (first template argument), the rank
  • one to associate an element to an other one (second template argument), the fathers

On an example :

std::vector<int>  rank (100);
std::vector<int>  parent (100);
boost::disjoint_sets<int*,int*> ds(&rank[0], &parent[0]);

Arrays are used &rank[0], &parent[0] to the type in the template is int*

For a more complex example (using maps) you can look at Ugo's answer.

You are just giving to the algorithm two structures to store the data (rank/parent) he needs.

寂寞笑我太脆弱 2024-10-08 12:43:29
disjoint_sets<Rank, Parent, FindCompress>

示例:

template <typename Rank, typename Parent>
void algo(Rank& r, Parent& p, std::vector<Element>& elements)
{
 boost::disjoint_sets<Rank,Parent> dsets(r, p);
 for (std::vector<Element>::iterator e = elements.begin();
      e != elements.end(); e++)
  dsets.make_set(*e);
  ...
}

int main()
{
  std::vector<Element> elements;
  elements.push_back(Element(...));
  ...

  typedef std::map<Element,std::size_t> rank_t; // => order on Element
  typedef std::map<Element,Element> parent_t;
  rank_t rank_map;
  parent_t parent_map;

  boost::associative_property_map<rank_t>   rank_pmap(rank_map);
  boost::associative_property_map<parent_t> parent_pmap(parent_map);

  algo(rank_pmap, parent_pmap, elements);
}

请注意,“Boost 属性映射库包含一些适配器,用于转换实现映射操作的常用数据结构,例如内置数组(指针)、迭代器和 std::map,以具有属性映射接口”

这些适配器的列表(如 boost::associative_property_map)可以在 此处

disjoint_sets<Rank, Parent, FindCompress>
  • Rank PropertyMap used to store the size of a set (element -> std::size_t). See union by rank
  • Parent PropertyMap used to store the parent of an element (element -> element). See Path compression
  • FindCompress Optional argument defining the find method. Default to find_with_full_path_compression See here (Default should be what you need).

Example:

template <typename Rank, typename Parent>
void algo(Rank& r, Parent& p, std::vector<Element>& elements)
{
 boost::disjoint_sets<Rank,Parent> dsets(r, p);
 for (std::vector<Element>::iterator e = elements.begin();
      e != elements.end(); e++)
  dsets.make_set(*e);
  ...
}

int main()
{
  std::vector<Element> elements;
  elements.push_back(Element(...));
  ...

  typedef std::map<Element,std::size_t> rank_t; // => order on Element
  typedef std::map<Element,Element> parent_t;
  rank_t rank_map;
  parent_t parent_map;

  boost::associative_property_map<rank_t>   rank_pmap(rank_map);
  boost::associative_property_map<parent_t> parent_pmap(parent_map);

  algo(rank_pmap, parent_pmap, elements);
}

Note that "The Boost Property Map Library contains a few adaptors that convert commonly used data-structures that implement a mapping operation, such as builtin arrays (pointers), iterators, and std::map, to have the property map interface"

This list of these adaptors (like boost::associative_property_map) can be found here.

爺獨霸怡葒院 2024-10-08 12:43:29

对于那些无法承受 std::map 的开销(或者因为类中没有默认构造函数而无法使用它)但数据又不那么简单的人作为int,我写了使用 std::vector 解决方案的指南,当您事先知道元素总数时,这是最佳选择。

该指南包含一个完整工作的示例代码,您可以使用它自行下载并测试。

那里提到的解决方案假设您可以控制类的代码,以便您可以添加一些属性。如果这仍然不可能,您总是可以在它周围添加一个包装器:

class Wrapper {
    UntouchableClass const& mInstance;
    size_t dsID;
    size_t dsRank;
    size_t dsParent;
}

此外,如果您知道元素的数量很小,则不需要 size_t,在这种情况下您可以添加一些模板对于 UnsignedInt 类型,并在运行时决定使用 uint8_tuint16_tuint32_tuint64_t 实例化它,您可以通过 在 C++11 中或使用 boost::cstdint 否则。

template <typename UnsignedInt>
class Wrapper {
    UntouchableClass const& mInstance;
    UnsignedInt dsID;
    UnsignedInt dsRank;
    UnsignedInt dsParent;
}

如果您错过了,这里再次提供链接: http://janoma .cl/post/using-disjoint-sets-with-a-vector/

For those of you who can't afford the overhead of std::map (or can't use it because you don't have default constructor in your class), but whose data is not as simple as int, I wrote a guide to a solution using std::vector, which is kind of optimal when you know the total number of elements beforehand.

The guide includes a fully-working sample code that you can download and test on your own.

The solution mentioned there assumes you have control of the class' code so that in particular you can add some attributes. If this is still not possible, you can always add a wrapper around it:

class Wrapper {
    UntouchableClass const& mInstance;
    size_t dsID;
    size_t dsRank;
    size_t dsParent;
}

Moreover, if you know the number of elements to be small, there's no need for size_t, in which case you can add some template for the UnsignedInt type and decide in runtime to instantiate it with uint8_t, uint16_t, uint32_tor uint64_t, which you can obtain with <cstdint> in C++11 or with boost::cstdint otherwise.

template <typename UnsignedInt>
class Wrapper {
    UntouchableClass const& mInstance;
    UnsignedInt dsID;
    UnsignedInt dsRank;
    UnsignedInt dsParent;
}

Here's the link again in case you missed it: http://janoma.cl/post/using-disjoint-sets-with-a-vector/

意犹 2024-10-08 12:43:29

Loic 的答案对我来说看起来不错,但我需要初始化父元素,以便每个元素都有自己作为父元素,因此我使用 iota 函数生成从 0 开始的递增序列。

使用 Boost,我为了简单起见,导入了bits/stdc++.h并使用了usingnamespacestd。

#include <bits/stdc++.h>

#include <boost/pending/disjoint_sets.hpp>
#include <boost/unordered/unordered_set.hpp>
using namespace std;

int main() {
  array<int, 100> rank;
  array<int, 100> parent;

  iota(parent.begin(), parent.end(), 0);
  boost::disjoint_sets<int*, int*> ds(rank.begin(), parent.begin());

  ds.union_set(1, 2);
  ds.union_set(1, 3);
  ds.union_set(1, 4);

  cout << ds.find_set(1) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(2) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(3) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(4) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(5) << endl;  // 5
  cout << ds.find_set(6) << endl;  // 6
}

我将 std::vector 更改为 std::array 因为将元素推送到向量将使其重新分配其数据,这使得不相交集对象包含的引用变得无效。

据我所知,不能保证父级将是一个特定的数字,所以这就是为什么我写了1或2或3或4(可以是其中任何一个)。也许文档更详细地解释了哪个数字将被选为该组的领导者(我还没有研究过)。

就我而言,输出是:

2
2
2
2
5
6

看起来很简单,它可能可以改进以使其更加健壮(以某种方式)。

注意:std::iota 用顺序递增的值填充范围 [first, last),从 value 开始并重复评估 ++value。
更多:https://en.cppreference.com/w/cpp/algorithm/iota

Loic's answer looks good to me, but I needed to initialize the parent so that each element had itself as parent, so I used the iota function to generate an increasing sequence starting from 0.

Using Boost, and I imported bits/stdc++.h and used using namespace std for simplicity.

#include <bits/stdc++.h>

#include <boost/pending/disjoint_sets.hpp>
#include <boost/unordered/unordered_set.hpp>
using namespace std;

int main() {
  array<int, 100> rank;
  array<int, 100> parent;

  iota(parent.begin(), parent.end(), 0);
  boost::disjoint_sets<int*, int*> ds(rank.begin(), parent.begin());

  ds.union_set(1, 2);
  ds.union_set(1, 3);
  ds.union_set(1, 4);

  cout << ds.find_set(1) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(2) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(3) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(4) << endl;  // 1 or 2 or 3 or 4
  cout << ds.find_set(5) << endl;  // 5
  cout << ds.find_set(6) << endl;  // 6
}

I changed std::vector to std::array because pushing elements to a vector will make it realloc its data, which makes the references the disjoint sets object contains become invalid.

As far as I know, it's not guaranteed that the parent will be a specific number, so that's why I wrote 1 or 2 or 3 or 4 (it can be any of these). Maybe the documentation explains with more detail which number will be chosen as leader of the set (I haven't studied it).

In my case, the output is:

2
2
2
2
5
6

Seems simple, it can probably be improved to make it more robust (somehow).

Note: std::iota Fills the range [first, last) with sequentially increasing values, starting with value and repetitively evaluating ++value.
More: https://en.cppreference.com/w/cpp/algorithm/iota

说不完的你爱 2024-10-08 12:43:29

我不久前写了一个简单的实现。看看吧。

struct DisjointSet {
    vector<int> parent;
    vector<int> size;

    DisjointSet(int maxSize) {
        parent.resize(maxSize);
        size.resize(maxSize);
        for (int i = 0; i < maxSize; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    int find_set(int v) {
        if (v == parent[v])
            return v;
        return parent[v] = find_set(parent[v]);
    }

    void union_set(int a, int b) {
        a = find_set(a);
        b = find_set(b);
        if (a != b) {
            if (size[a] < size[b])
                swap(a, b);
            parent[b] = a;
            size[a] += size[b];
        }
    }
};

用法是这样的。这很简单。不是吗?

void solve() {
    int n;
    cin >> n;
    DisjointSet S(n);  // Initializing with maximum Size
    S.union_set(1, 2);
    S.union_set(3, 7);
    int parent = S.find_set(1);  // root of 1
}

I written a simple implementation a while ago. Have a look.

struct DisjointSet {
    vector<int> parent;
    vector<int> size;

    DisjointSet(int maxSize) {
        parent.resize(maxSize);
        size.resize(maxSize);
        for (int i = 0; i < maxSize; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    int find_set(int v) {
        if (v == parent[v])
            return v;
        return parent[v] = find_set(parent[v]);
    }

    void union_set(int a, int b) {
        a = find_set(a);
        b = find_set(b);
        if (a != b) {
            if (size[a] < size[b])
                swap(a, b);
            parent[b] = a;
            size[a] += size[b];
        }
    }
};

And the usage goes like this. It's simple. Isn't it?

void solve() {
    int n;
    cin >> n;
    DisjointSet S(n);  // Initializing with maximum Size
    S.union_set(1, 2);
    S.union_set(3, 7);
    int parent = S.find_set(1);  // root of 1
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文