算法学习之路(4)--- 并查集

实现并查集

并查集的定义就是(非严格):

  • 每个不同集合的有一个代表节点
  • 同一个集合内的对象的父节点指向该代表节点
  • 该代表节点的父节点指向自己

并查集的相关注意:

  • 用户必须一次性给出所有的节点,将这些节点放入并查集中
  • 并查集存放的是元素,他是在以一种特殊的方式组织这些元素,使得某些操作变得简单

并查集的基本操作:

  • 合并:两个集合合并,数量小的集合将挂载在大的集合的代表节点上
  • 查找:给出两个节点,判断他们是否在同一个集合中。
  • 优化:在向上查找的过程中,会将沿途走过的节点直接挂载在代表节点下,可以减少下次的查找时间

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没有放入,父节点是自己

   }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值