一、主要步骤:
并查集,是用来求两者是否同属一个单位的一种思路。
仅由一个内容仅仅两行左右的函数以及一个数组就可以实现。
优点:方便快捷,使用有效。
缺点:emmm……没有什么大的缺点,非要有的话可能就是时间复杂度有些高,
但是这个是可以通过一个小优化解决哒!
1.有若干个样本a,b,c,d......的类型为V
2.在并查集中一开始认为每个样本都在单独的集合里面
3.用户可以在任何时候调用两个方法
bool isSameSet(V x , V y) :查询样本x,y是否属于一个集合
void union(V x,V y) :把x,y所在的所有样本合并为一个集合
4.两个算法代价越低越好(需要对算法进行优化)
二、为什么要用并查集
并查集是用来判断两者是否为一个单位的方法。
如果同属于一个单位的人为一个战队,那么通过标记和函数查找,我们可以清楚地知道这一个战队的队长是谁,并且以这个队长为这个战队的标志,以后战队的每一个人与其他人碰面,只要标志不同,那就把另一个拉近自己的战队。在访问的时候,只要两个人的标志不同,就是不在一个战队,否则他们就是一个战队的好哥们啦。
并查集:
<1> 定义
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。
<2>初始化
把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
<3>查找
查找元素所在的集合,即根节点。
<4>合并
将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的"查找"操作实现。
三、代码的实现
说明:
parents代表父节点,即代表点
node<>代表节点
sizemap代表以此节点为代表点的集合的长度,用于判断哪个集合连在哪个集合后面
1.找代表点
初始情况的所有点的代表点都是自己,在后续合并的过程中,不是代表的点的代表点会发生变化,这时候,只需一直往上找,直到找到一个节点的代表点是自己,那么就找到头了,就代表找到代表点了,伪代码如下:
Node<V> findFather(Node<N> cur){找到代表点
stack< Node<V> > path = new stack<>();
while ( cur!=parents.get(cur) ){//找到代表点
path.push( cur );
cur = parents.get(cur);
}
//cur头结点
while ( !path.isEmpty() ){//找到之后,沿途所有的点的代表点都要变成cur
parents.put( path.pop() , cur);
}
return cur;
}
2.判断a,b是否在同一个集合中
判断两个点是否在一个集合中,只需要判断a,b的代表点的地址是否相同,就可以说明他们是否在同一个集合,伪代码如下:
void isSameSet (V a, V b){//是否属于同一集合
if( !nodes.containsKey(a) || !nodes.containsKey(b) ){//没有初始化
return false;
}
return findFather(nodes.get(a)) == findFather(nodes.get(b));
//a的代表点的地址等于b的代表点的地址,则返回true,否则返回false
}
3.合并a,b所在的集合
合并的时候,首先要判断a,b是否在同一集合内,如果在就不需要合并,如果不在,则把小集合挂在大集合的后面,伪代码如下:
void union(V a,V b,){//合并a,b所在的集合
if( !nodes.containsKey(a) || !nodes.containsKey(b) ){//没有初始化
return ;
}
//a的代表点为ahead,b的代表点为bhead
Node<V> aHead = findFather( nodes.get(a) );
Node<V> aHead = findFather( nodes.get(a) );
if(aHead != bHead){
//sizeMap表示代表点的集合的长度
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
Node<V> big = aSetSize >=bSetSize ? aHead : bHead;
Node<V> small = big==aHead ? bHead : aHead;
parents.put( small , big);//小的直接把父亲改成aHead;
sizeMap.put( big,aSetSize + bSetSize );
sizeMap.remove( small );
/* if(aSetSize >= bSetSize){//小挂大
parents.put( bHead , aHead);//小的直接把父亲改成aHead;
sizeMap.put( aHead,aSetSize + bSetSize );
sizeMap.remove( bHead );
}
else{
parents.put( aHead , bHead);//小的直接把父亲改成bHead;
sizeMap.put( bHead,aSetSize + bSetSize );
sizeMap.remove( aHead );
*/
}
}
}