パス圧縮を使用して UnionFind 構造アルゴリズムを実装しようとすると、(stackoverflow (hehe) ではなく) Find アルゴリズムに問題があります。
私はintの標準配列を持っていますが、配列はかなり大きくなる可能性があります-> 60.000.000要素までは正常に動作します。
私のユニオン関数は次のようになります。
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--;
}
}
私の 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;
}
私は検索で反復的な方法を試しました:
public int find(int i) {
while (i != id[i]){
id[i] = id[id[i]];
i = id[i];
}
return i;
}
および末尾再帰:
public int find(int i) {
int p = id[i];
if (i == p) {
return i;
}
return id[i] = find(p);
}
私のコードで見逃したものはありますか? この種の問題に対する他のアプローチはありますか?
@edit: コンストラクターをコードに追加:
public UnionFind(int N) {
stevilo = N;
id = new int[N];
for(int i = 0; i < N; i++){
id[i] = i;
}
@edit2(より良い説明と新しい発見):60.000.000要素未満の場合、問題はstackoverflowになくなりました。これは、私の問題を解決するのに十分です。
私は次のようにテストユニオンを呼び出しています:
for(i=0;i<id.length-1;i++)
unite(i,i+1)
したがって、エンディングのペアは次のようになります。
0:1, 1:2, 2:3, 3:4,..
これは、手段のみをテストするための最も最適でないオプションの例にすぎません:)
次に、0 の代表がテーブルの最後の要素 (100 要素の場合は 99) であるかどうかを確認し、それが機能するようにします。
問題は、初期要素がそれぞれ独自の結合 (0:0、1:1、2:2、3:3) にある場合にのみ、私のアルゴリズムが機能することです。すでに別のユニオン (0:2、1:6、2:1、3:5、...) を設定している場合、テスト アルゴリズムが機能しなくなります。
私はそれを検索機能の問題に絞り込みました。おそらくパス圧縮に関係しています
id[i] = id[id[i]].