table of Contents
@ (Article directories)
Understanding disjoint-set
For 并查集(不相交集合)
a lot of people will feel very 陌生
, heard or not particularly aware of . Indeed disjoint-set is a very efficient data structure. Simple , but all the elements of the system 一遵从一个规律
so efficient and let them do things efficiently.
For certain sense, such a definition on Wikipedia:
Disjoint-set, a set of applications in a number of issues have N elements, we usually make at the beginning of each element of the collection constitute a single element , then a certain order will belong to the set of elements of the same consolidated group where , during to repeatedly to find an element in which the collection. It features does not seem complicated, but the amount of data is great, if your normal data structure to describe it, often in the space is too large, the computer can not afford; even in the space of barely passed, the time complexity of the operation is also very high, impossible to describe the results of calculating the desired questions, and can only be used to check the game sets a predetermined operation time of (1 to 3 seconds).
It is a disjoint-set 树型的数据结构
for dealing with some disjoint sets (Disjoint Sets) mergers and inquiries . Often use to forest to represent.
And check assembly resolution
The basic idea
- Initialization , a forest each as an independent. Usually represented by an array, each initial value of -1.
各自为根
join(a,b)
operating. a, b two集合
combined. Note here is a, not a, b merge, but merge the set of a, b's. This spawns some cases:- a, b if it is a separate (and no other consolidated) , then directly to a point b (point a or b), namely
data[a]=b
; this set in order to express the number of original-1
. Again -1 i.e., bdata[b]=-2
. b is expressed in father node has | -2 | months.
- a, b if there is a set of (possibly his father, he is probably the root) , then we of course
不能直接操作a,b
(because a, b may have been pointing to someone else.) So we can only operate a, b ancestors . Because a, b ancestors is not directed ( i.e., a negative value indicates the size of data ). So they first a negative value to be added on top of another to go. In addition to this value becomes the point of contact that represents.
For the above you may be in doubt:
How can I see a, b whether in a collection?
- Whether viewed in a collection, just to see
节点根祖先的结果是否相同即可
. Because only root value is negative , while others are positive elements representing points. So it is necessary only一直寻找直到不为正数进行比较即可
!
a, b merger, whether it is a merger of the ancestors in the ancestral b, or b ancestors on a merger?
- Here will encounter both cases, the choice is very important. You have to understand one thing: +1 height of the tree so the efficiency of the elements of the query will be reduced!
So we usually are: a decimal point to the tree (the tree pointing to a higher or lower tree), this makes the query efficiency can be increased!
Of course, the height and number on choice, you also need your own choice and consideration.
Other paths compression?
Each query, from the bottom up. When we call the recursive compression path can be the way, because we really only need to find one element until its ancestors, so when he was closer ancestor so 下次查询就很快
. And the cost of compression path is not great!
Code
Disjoint-set is relatively simple to implement, directly attached to the code!
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)); //大小
}
}
Epilogue
- Disjoint-set are simple but very efficient data structure. In the often encountered in the collection . If you do not use disjoint-set while the traditional efficiency violence is too low, without being adopted.
- In addition,
并查集还广泛用于迷宫游戏
the following have the opportunity to introduce a disjoint-set to achieve a maze game . We welcome attention! - Finally, I welcome everyone's attention the public number, learn together, exchange!
笔者学习资源
Also placed in the public numbers and share with everyone!