ディレクトリ
@(記事のディレクトリ)
互いに素セットを理解します
以下のために并查集(不相交集合)
多くの人々は非常に感じるだろう陌生
、聞いたかの特に意識していません。実際、互いに素-セットは非常に効率的なデータ構造です。シンプルな、しかし、システムのすべての要素一遵从一个规律
ので、効率的かつそれらを効率的に物事をやらせます。
ある意味では、Wikipediaのような定義:
ディスジョイントセット、問題の数のアプリケーションのセットは、N個の要素を持って、我々は通常、単一の要素を構成する、コレクションの各要素の先頭で行い、その後、特定の順序は同じ連結グループの要素の集合に属します、の中繰り返し見つけるために、コレクション内の要素を。それは複雑なようではありませんが、データの量は、通常のデータ構造はスペースが大きすぎることが多い中で、それを記述するために、コンピュータは余裕がない場合には、素晴らしいです。でも、かろうじて合格の空間に、操作の時間計算量も非常にあります、高い所望の質問を計算した結果を説明することは不可能とだけゲームをチェックするために使用することができる(1〜3秒)の所定の動作時間を設定します。
互いに素設定である树型的数据结构
ため、いくつかの互いに素な集合(互いに素な集合)合併やお問い合わせを扱います。多くの場合に使用森表現するために。
そして、総会決議をチェック
基本的な考え方
- 初期化、独立したとして森それぞれ。通常、アレイ、-1の各初期値で表されます。
各自为根
join(a,b)
操作。、B 2集合
合成。ここで注意してください、ではない、Bのマージですが、Bさんのセットをマージします。これは、いくつかのケースを生成します:- 、もしbが、別個の(なし他の連結)は、直接に、B点、すなわち(a点またはb)
data[a]=b
;元の数を表現するために、このセット-1
。再び-1即ち、Bdata[b]=-2
。Bがで表され父ノードが持つ| -2 |ヶ月。
- 、のセットがあれば(おそらく彼の父は、彼はおそらくルートである)、Bコースの私たちは、その後、
不能直接操作a,b
(、bは。他の誰かを指してきた可能性があるため)だから我々は唯一、B先祖を操作することができます。、Bの祖先が指示されていないため(すなわち、負の値は、データのサイズを示します)。行くために、別の上に追加される最初の負の値、彼らはそう。この値に加えて表し接触点となります。
上記の場合、あなたは間違いであってもよいです。
どのように私は、コレクションにするかどうかB、見ることができますか?
- ちょうど見て、コレクションに表示するかどうか
节点根祖先的结果是否相同即可
。ためだけルート値が負である他のポイントを表す正の要素であるが、。だから、必要なだけです一直寻找直到不为正数进行比较即可
!
、Bの合併、それは合併の先祖Bで先祖の合併、またはbの祖先であるかどうか?
- ここでは両方のケースが発生します、選択は非常に重要です。クエリの要素の効率が低下しますので、木の高さは1:あなたは一つのことを理解する必要があります!
だから我々は、通常は以下のとおりです。木(ツリーは、より高いまたはより低い木を指す)に小数点が、これは、クエリの効率を向上させることができます!
もちろん、高さと数の選択には、また、あなた自身の選択と配慮を必要としています。
他のパス圧縮?
ボトムアップからの各クエリ、。我々は再帰的な圧縮パスを呼び出すときに私たちは本当にだけなので、その祖先まで一つの要素を見つける必要があるため、方法することができ、彼は近い祖先だったので下次查询就很快
。そして、圧縮パスのコストは大きくありません!
コードの実装
互いに素-セットは直接コードに取り付けた、実装が比較的簡単です!
package 并查集不想交集合;
import java.util.Scanner;
public class DisjointSet {
static int tree[]=new int[100000];//假设有500个值
public DisjointSet() {set(this.tree);}
public DisjointSet(int tree[])
{
this.tree=tree;
set(this.tree);
}
public void set(int a[])//初始化所有都是-1 有两个好处,这样他们指向-1说明是自己,第二,-1代表当前森林有-(-1)个
{
int l=a.length;
for(int i=0;i<l;i++)
{
a[i]=-1;
}
}
public int search(int a)//返回头节点的数值
{
if(tree[a]>0)//说明是子节点
{
return tree[a]=search(tree[a]);//路径压缩
}
else
return a;
}
public int value(int a)//返回a所在树的大小(个数)
{
if(tree[a]>0)
{
return value(tree[a]);
}
else
return -tree[a];
}
public void union(int a,int b)//表示 a,b所在的树合并
{
int a1=search(a);//a根
int b1=search(b);//b根
if(a1==b1) {System.out.println(a+"和"+b+"已经在一棵树上");}
else {
if(tree[a1]<tree[b1])//这个是负数,为了简单减少计算,不在调用value函数
{
tree[a1]+=tree[b1];//个数相加 注意是负数相加
tree[b1]=a1; //b树成为a的子树,直接指向a;
}
else
{
tree[b1]+=tree[a1];//个数相加 注意是负数相加
tree[a1]=b1; //b树成为a的子树,直接指向a;
}
}
}
public static void main(String[] args)
{
DisjointSet d=new DisjointSet();
d.union(1,2);
d.union(3,4);
d.union(5,6);
d.union(1,6);
d.union(22,24);
d.union(3,26);
d.union(36,24);
System.out.println(d.search(6)); //头
System.out.println(d.value(6)); //大小
System.out.println(d.search(22)); //头
System.out.println(d.value(22)); //大小
}
}
エピローグ
- 互いに素-セットはシンプルですが、非常に効率的なデータ構造です。では、多くの場合、コレクションに遭遇しました。あなたが使用していない場合は、伝統的な効率の暴力が採用されることなく、低すぎる一方、互いに素セット。
- また、
并查集还广泛用于迷宫游戏
以下の互いに素-設定を達成するために紹介する機会持つ迷路ゲームを。私たちは注意を歓迎します! - 最後に、私はみんなの注目は、公開数は、一緒に学ぶ歓迎し、交流!
笔者学习资源
また、公共の数字に入れて、みんなで共有!