我正在尝试实现在Kruskal算法中使用的不相交集合,但我很难确切地理解应该如何做到这一点,特别是如何管理树木森林。在阅读了维基百科对不相交集合的描述之后,在阅读了算法导论(Cormen et al)中的描述之后,我得出了以下结论:
class DisjointSet
{
public:
class Node
{
public:
int data;
int rank;
Node* parent;
Node() : data(0),
rank(0),
parent(this) { } // the constructor does MakeSet
};
Node* find(Node*);
Node* merge(Node*, Node*); // Union
};
DisjointSet::Node* DisjointSet::find(DisjointSet::Node* n)
{
if (n != n->parent) {
n->parent = find(n->parent);
}
return n->parent;
}
DisjointSet::Node* DisjointSet::merge(DisjointSet::Node* x,
DisjointSet::Node* y)
{
x = find(x);
y = find(y);
if (x->rank > y->rank) {
y->parent = x;
} else {
x->parent = y;
if (x->rank == y->rank) {
++(y->rank);
}
}
}
我很确定这是不完整的,我遗漏了一些东西。
算法简介提到应该有一个树木森林,但它没有为这个森林的实际实现提供任何解释。我观看了CS61B第31课:不相交集合( http://www.youtube.com/watch?v=wSPAjGfDl7Q ),在这里,讲师只使用一个数组来存储森林及其所有树和值。没有像我提到的那样明确的“Node”类型的类。我还发现了许多其他的来源(我不能发布超过一个链接),它们也使用这种技术。我很乐意这样做,但这依赖于数组的索引进行查找,而且因为我想存储int以外的类型的值,所以我需要使用其他类型的值(想到的是std::map)。
另一个我不确定的问题是内存分配,因为我使用的是C++。我正在存储指针树,我的MakeSet操作将是: new DisjointSet::Node;。现在,这些节点只有指向其父节点的指针,所以我不确定如何找到树的底部。我怎样才能遍历我的树来释放它们呢?
我理解这种数据结构的基本概念,但我只是对它的实现有点困惑。我们非常欢迎您的意见和建议,谢谢。
发布于 2010-12-21 20:36:47
无论如何都不是一个完美的实现(毕竟是我写的!),但这有帮助吗?
/***
* millipede: DisjointSetForest.h
* Copyright Stuart Golodetz, 2009. All rights reserved.
***/
#ifndef H_MILLIPEDE_DISJOINTSETFOREST
#define H_MILLIPEDE_DISJOINTSETFOREST
#include <map>
#include <common/exceptions/Exception.h>
#include <common/io/util/OSSWrapper.h>
#include <common/util/NullType.h>
namespace mp {
/**
@brief A disjoint set forest is a fairly standard data structure used to represent the partition of
a set of elements into disjoint sets in such a way that common operations such as merging two
sets together are computationally efficient.
This implementation uses the well-known union-by-rank and path compression optimizations, which together
yield an amortised complexity for key operations of O(a(n)), where a is the (extremely slow-growing)
inverse of the Ackermann function.
The implementation also allows clients to attach arbitrary data to each element, which can be useful for
some algorithms.
@tparam T The type of data to attach to each element (arbitrary)
*/
template <typename T = NullType>
class DisjointSetForest
{
//#################### NESTED CLASSES ####################
private:
struct Element
{
T m_value;
int m_parent;
int m_rank;
Element(const T& value, int parent)
: m_value(value), m_parent(parent), m_rank(0)
{}
};
//#################### PRIVATE VARIABLES ####################
private:
mutable std::map<int,Element> m_elements;
int m_setCount;
//#################### CONSTRUCTORS ####################
public:
/**
@brief Constructs an empty disjoint set forest.
*/
DisjointSetForest()
: m_setCount(0)
{}
/**
@brief Constructs a disjoint set forest from an initial set of elements and their associated values.
@param[in] initialElements A map from the initial elements to their associated values
*/
explicit DisjointSetForest(const std::map<int,T>& initialElements)
: m_setCount(0)
{
add_elements(initialElements);
}
//#################### PUBLIC METHODS ####################
public:
/**
@brief Adds a single element x (and its associated value) to the disjoint set forest.
@param[in] x The index of the element
@param[in] value The value to initially associate with the element
@pre
- x must not already be in the disjoint set forest
*/
void add_element(int x, const T& value = T())
{
m_elements.insert(std::make_pair(x, Element(value, x)));
++m_setCount;
}
/**
@brief Adds multiple elements (and their associated values) to the disjoint set forest.
@param[in] elements A map from the elements to add to their associated values
@pre
- None of the elements to be added must already be in the disjoint set forest
*/
void add_elements(const std::map<int,T>& elements)
{
for(typename std::map<int,T>::const_iterator it=elements.begin(), iend=elements.end(); it!=iend; ++it)
{
m_elements.insert(std::make_pair(it->first, Element(it->second, it->first)));
}
m_setCount += elements.size();
}
/**
@brief Returns the number of elements in the disjoint set forest.
@return As described
*/
int element_count() const
{
return static_cast<int>(m_elements.size());
}
/**
@brief Finds the index of the root element of the tree containing x in the disjoint set forest.
@param[in] x The element whose set to determine
@pre
- x must be an element in the disjoint set forest
@throw Exception
- If the precondition is violated
@return As described
*/
int find_set(int x) const
{
Element& element = get_element(x);
int& parent = element.m_parent;
if(parent != x)
{
parent = find_set(parent);
}
return parent;
}
/**
@brief Returns the current number of disjoint sets in the forest (i.e. the current number of trees).
@return As described
*/
int set_count() const
{
return m_setCount;
}
/**
@brief Merges the disjoint sets containing elements x and y.
If both elements are already in the same disjoint set, this is a no-op.
@param[in] x The first element
@param[in] y The second element
@pre
- Both x and y must be elements in the disjoint set forest
@throw Exception
- If the precondition is violated
*/
void union_sets(int x, int y)
{
int setX = find_set(x);
int setY = find_set(y);
if(setX != setY) link(setX, setY);
}
/**
@brief Returns the value associated with element x.
@param[in] x The element whose value to return
@pre
- x must be an element in the disjoint set forest
@throw Exception
- If the precondition is violated
@return As described
*/
T& value_of(int x)
{
return get_element(x).m_value;
}
/**
@brief Returns the value associated with element x.
@param[in] x The element whose value to return
@pre
- x must be an element in the disjoint set forest
@throw Exception
- If the precondition is violated
@return As described
*/
const T& value_of(int x) const
{
return get_element(x).m_value;
}
//#################### PRIVATE METHODS ####################
private:
Element& get_element(int x) const
{
typename std::map<int,Element>::iterator it = m_elements.find(x);
if(it != m_elements.end()) return it->second;
else throw Exception(OSSWrapper() << "No such element: " << x);
}
void link(int x, int y)
{
Element& elementX = get_element(x);
Element& elementY = get_element(y);
int& rankX = elementX.m_rank;
int& rankY = elementY.m_rank;
if(rankX > rankY)
{
elementY.m_parent = x;
}
else
{
elementX.m_parent = y;
if(rankX == rankY) ++rankY;
}
--m_setCount;
}
};
}
#endif
发布于 2013-10-10 00:38:48
发布于 2013-10-10 01:15:49
你的实现看起来不错(除了在合并函数中,你要么声明返回void,要么在那里放一个return,我更喜欢返回void)。问题是你需要追踪Nodes*
。您可以通过在DisjointSet
类上使用vector<DisjointSet::Node*>
或在其他地方使用此vector
并将DisjointSet
的方法声明为static
来完成此操作。
以下是run的示例(请注意,我将merge更改为返回空,并且没有将DisjointSet
的方法更改为static
int main()
{
vector<DisjointSet::Node*> v(10);
DisjointSet ds;
for (int i = 0; i < 10; ++i) {
v[i] = new DisjointSet::Node();
v[i]->data = i;
}
int x, y;
while (cin >> x >> y) {
ds.merge(v[x], v[y]);
}
for (int i = 0; i < 10; ++i) {
cout << v[i]->data << ' ' << v[i]->parent->data << endl;
}
return 0;
}
使用此输入:
3 1
1 2
2 4
0 7
8 9
它打印预期的:
0 7
1 1
2 1
3 1
4 1
5 5
6 6
7 7
8 9
9 9
你的森林是由树木组成的:
7 1 5 6 9
/ / | \ |
0 2 3 4 8
所以你的算法很好,据我所知,你有最好的联合查找的复杂度,并且你在你的vector
上跟踪你的Node
。因此,您可以简单地解除分配:
for (int i = 0; i < int(v.size()); ++i) {
delete v[i];
}
https://stackoverflow.com/questions/4498833
复制相似问题