[英]Dealing with Union-Find algorithms with a lot of objects
I have a problem with (not anymore with stackoverflow (hehe)) Find algorithm when trying to implement UnionFind structure algorithm with path-compression. 我有一个问题(不再使用stackoverflow(hehe))尝试使用路径压缩实现UnionFind结构算法时查找算法。
I have standard array of ints, array can get pretty big -> it works fine until 60.000.000 elements. 我有标准的int数组,数组可以变得很大 - >它工作正常,直到60.000.000元素。
My Union function looks like this: 我的联盟功能如下所示:
public void unite(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length){
if (isInSameSet(p, q)) return;
id[find(p)] = find(q);
stevilo--;
}
}
My isInSameSet looks like this: 我的isInSameSet看起来像这样:
public boolean isInSameSet(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length)
return find(p) == find(q);
return false;
}
I have tried iterative way in Find: 我在Find中尝试了迭代方式:
public int find(int i) {
while (i != id[i]){
id[i] = id[id[i]];
i = id[i];
}
return i;
}
and tail-recrusion: 和尾巴后退:
public int find(int i) {
int p = id[i];
if (i == p) {
return i;
}
return id[i] = find(p);
}
Is there anything I missed in my code? 我的代码中有什么错过的吗? Is there any other approach to this kind of problems?
有没有其他方法来解决这类问题?
@edit: Adding constructor to code: @edit:为代码添加构造函数:
public UnionFind(int N) {
stevilo = N;
id = new int[N];
for(int i = 0; i < N; i++){
id[i] = i;
}
@edit2 (better explanation and new findings): The problem is not in stackoverflow anymore for less then 60.000.000 elements, which is more then enough for solving my problems. @ edit2(更好的解释和新的发现):问题不再是在stackoverflow中,不到60.000.000元素,这足以解决我的问题。
I'm calling test Unions like this: 我打电话给这样的测试工会:
for(i=0;i<id.length-1;i++)
unite(i,i+1)
so the ending pairs are like this: 所以结束对是这样的:
0:1, 1:2, 2:3, 3:4,..
which is only example of least optimal option for testing means only :) 这只是测试手段的最佳选择的唯一例子:)
Then I check if representative of 0 is last element in table (99 for 100 elements) and it works. 然后我检查0的代表是否是表中的最后一个元素(99个表示100个元素)并且它有效。
Problem is, that my algorithm works only if initial elements are each in their own union (0:0, 1:1, 2:2, 3:3). 问题是,我的算法只有在初始元素各自都在它们自己的并集中时才有效(0:0,1:1,2:2,3:3)。 If I have different Unions already set up (0:2, 1:6, 2:1, 3:5, ...) my testing algorithm stops working.
如果我已经设置了不同的联盟(0:2,1:6,2:1,3:5,......),我的测试算法将停止工作。
I have narrow it down to a problem in Find function, probably something to do with path compression 我已将其缩小到Find函数中的问题,可能与路径压缩有关
id[i] = id[id[i]].
One small optimization would be to get rid of isInSameSet... 一个小的优化就是摆脱isInSameSet ......
public void unite(int p, int q) {
if(p >= 0 && p < id.length && q >= 0 && q < id.length){
int rootp = find(p);
int rootq = find(q);
if (rootp==rootq) return;
id[rootp] = rootq;
stevilo--;
}
}
Union-Find data structures typically include TWO different optimizations. Union-Find数据结构通常包括两种不同的优化。 One is path compression.
一个是路径压缩。 You have that.
你有。
But the other optimization happens during a Union, where you carefully choose which of the two roots to make a child of the other, usually via Union-By-Rank or Union-By-Size. 但是其他优化发生在联盟中,在那里你仔细选择两个根中的哪一个来生成另一个的孩子,通常是通过Union-By-Rank或Union-By-Size。 With that optimization, your trees should never be deep enough to get a stack overflow.
通过该优化,您的树永远不应该足够深,以获得堆栈溢出。 However, that optimization seems to be missing from your unite function.
但是,您的联合功能似乎缺少这种优化。
I once wrote an algorithm for UnionFind
, and its time complexity is O(log*(n)). 我曾经为
UnionFind
编写了一个算法,其时间复杂度为O(log *(n))。 Thats iterative logarithm of n. 那是n的迭代对数。 The algorithm compresses the path of the tree as it keeps on connecting the nodes to gain efficiency.
该算法在继续连接节点以提高效率时压缩树的路径。 I find it very efficient, though I haven't practically tested it against huge array size.
我发现它非常有效,尽管我没有对巨大的阵列大小进行实际测试。 Here's the code:
这是代码:
public class UnionFind
{
private int[] id;
public UnionFind(int capacity)
{
id = new int[capacity];
for (int i = 0; i < capacity; i++)
{
id[i] = i;
}
}
public boolean isConnected(int p, int q)
{
return root(p) == root(q);
}
public void connect(int p, int q)
{
if (isConnected(p, q))
{
return;
}
id[root(p)] = root(q);
}
private int root(int p)
{
int temp = p;
if (p != id[p] && id[id[p]] != id[p])
{
while (p != id[p])
{
p = id[p];
}
id[temp] = id[p];
}
return id[p];
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.