实现并查集
并查集的定义就是(非严格):
- 每个不同集合的有一个代表节点
- 同一个集合内的对象的父节点指向该代表节点
- 该代表节点的父节点指向自己
并查集的相关注意:
- 用户必须一次性给出所有的节点,将这些节点放入并查集中
- 并查集存放的是元素,他是在以一种特殊的方式组织这些元素,使得某些操作变得简单
并查集的基本操作:
- 合并:两个集合合并,数量小的集合将挂载在大的集合的代表节点上
- 查找:给出两个节点,判断他们是否在同一个集合中。
- 优化:在向上查找的过程中,会将沿途走过的节点直接挂载在代表节点下,可以减少下次的查找时间
code:
package 基础算法;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
//for test
class node
{
public int data;
node(int s)
{
this.data =s;
}
}
public class UnionSet<T> {
//为了方便测试,将这两个hashmap设为public,实际中应该设为private
public HashMap<T, T> hm1 = new HashMap(); //用来记录父子关系的hashmap
public HashMap<T, Integer> hm2 = new HashMap();//用来记录一个集合中的元素个数
//并查集初始化
public UnionSet(List<T> ls)
{
for(T t : ls)
{
hm1.put(t,t);//初始的并查集中每个元素的父节点是自己
hm2.put(t,1);//初始的并查集中每个集合只有一个成员
}
}
//并查集合并:
public void union(T t1, T t2)
{
T father1 = findPresents(t1);
T father2 = findPresents(t2);
//temp指向将要挂载的节点,即元素个数少的集合的代表节点
T temp = hm2.get(father1) > hm2.get(father2) ? father2 : father2;
T temp1 = temp == father1 ? father2 : father1; //temp1 指向元素个数大的节点
hm1.put(temp,temp1); //将元素个数少的集合融入元素个数大的集合中 temp1是新的集合的代表节点
hm2.put(temp1,hm2.get(temp)+hm2.get(temp1));
//for test
System.out.println("counts="+hm2.get(temp1));
}
public T findPresents(T n1)
{
T temp = n1;
while(hm1.get(temp) != temp)
{
temp = hm1.get(temp);
}
return temp;
}
//并查集查找
//优化查找操作:
public boolean isInSameSet(T t1, T t2)
{
return findAndReviseProcess(t1) == findAndReviseProcess(t2);
}
public T findAndReviseProcess(T t1)
{ T father;
if(hm1.get(t1) == t1)
return t1;
father = findAndReviseProcess(hm1.get(t1));
hm1.put(t1,father);
return father;
}
//for test
public static void main(String[] args) {
ArrayList<node> ls = new ArrayList<>();
for(int i = 0; i< 6; i++)
{
ls.add(new node(i));
}
UnionSet<node> us = new UnionSet(ls);
us.union(ls.get(1),ls.get(2));
us.union(ls.get(1),ls.get(3));
us.union(ls.get(1),ls.get(4));
System.out.println(us.findPresents(ls.get(1)).data);//集合的代表节点是1
System.out.println(us.isInSameSet(ls.get(1),ls.get(3)));//是同一个集合
System.out.println(us.isInSameSet(ls.get(1),ls.get(5)));//不是同一集合,因为没有放入5
System.out.println(us.isInSameSet(ls.get(1),ls.get(4)));//是同一个结合
System.out.println(us.hm1.get(ls.get(1)).data);//结果是1
System.out.println(us.hm1.get(ls.get(2)).data);//结果是1
System.out.println(us.hm1.get(ls.get(3)).data);//结果是1
System.out.println(us.hm1.get(ls.get(4)).data);//结果是1
System.out.println(us.hm1.get(ls.get(5)).data);//结果是5,因为5没有放入,父节点是自己
}
}