并查集可以使用 数组,链表实现,但是复杂度较高。比如一个集合的数据都存储在一个数组或者链表中,那么查找元素是否在该链表就是一个数组遍历的过程,进行集合的并集也就成了将一个数组中的元素赋值到另一个数组中,无论是查找还是合并,他的复杂度都是趋于O(N)的。
较为合理的方法是使用HashMap表示集合状态,我们给每一个集合设置一个头部节点。HashMap中 key表示该节点,value表示
该节点的头部节点,就把他想象成一个链表 。比如map中三组数据 (1,1)(2,1) (3,2) 含义就是1 是该集合的头部节点,2的父节点都是1 3的父节点是2,他们三个构成1个集合。设计头部节点,这样比较两个数是否在一个集合或者两个集合合并时候 直接操作头部节点即可。
1.判断两个节点是否在一个集合,就判断他们的头部节点是否一样即可。
2.查找一个节点的头部节点就是递归的查找的过程,这里边涉及了一个优化,
比如 集合 状态是 1是头部节点 1与2 相连 1与3相连 那么想找到3 是要循环两次的。优化则是在第一次找3 的时候,发现3没有直接与1 相连,然后让的parent直接指向1。
合并的话就直接把元素较小的集合的头部指向元素大集合的头部即可。元素的个数都记录在另一个hashmap中。
ublic class BingChaJi {
/**
*
* 并查集可以使用简单的列表来描述,一个列表表示一个集合,但是这样的话查找一个集合时间复杂度为O(N)
* 另外如果实现两个集的合并,个数小的集合要完全遍历,成本太高
* 这样我们可以使用HashMap Key值表示当前节点。value表示他的父节点
* -------------------属性设计-----------------------
* Hashmap nodeMap Key值表示当前节点。value表示他的父节点
* HashMap nodeNum KEY表示当前节点,VALUE表示他有多少哥子节点 用于合并的时候判断哪个呗合并
* -------------------属性设计-----------------------
* 1. findParent(K key) 找到该节点最顶节点,并将该节点以上节点全部直接接到头结点上
* 2. isOne(K KEY1,K KEY2) 查看两个的最顶节点是否在一个集合
* 3. Union(K KEY1,K key2 ) 对两个集合进行融合
*/
HashMap nodeMap ;
HashMap nodeNum;
public BingChaJi(){};
public <T>BingChaJi(T []list){
for(T temp:list){
nodeMap.put(temp,temp); //将头指向他自己
nodeNum.put(temp,1); //设置他的节点个数为1
}
}
public <T>T findParent(T node ){
T parent =(T)nodeMap.get(node);
if(parent!=node){
parent =findParent(parent);
}
nodeMap.put(node,parent);
return parent;
}
public <T> boolean isOne(T nodeOne,T nodeTwo){
boolean is =findParent(nodeOne)==findParent(nodeTwo);
return is;
}
public <T> void Union(T nodeOne ,T nodeTwo){
T parentOne =findParent(nodeOne);
T parentTwo =findParent(nodeTwo);
if(parentOne!=parentTwo){
int sizeOne =(int)nodeNum.get(nodeOne);
int sizeTwo =(int) nodeNum.get(nodeTwo);
if(sizeTwo<sizeOne){
nodeMap.put(parentTwo,parentOne);
nodeNum.put(parentOne,sizeOne+sizeTwo);
}else{
nodeMap.put(parentTwo,parentOne);
nodeNum.put(parentTwo,sizeOne+sizeTwo);
}
}
return ;
}
}