并查集算法

一、主要步骤:

并查集,是用来求两者是否同属一个单位的一种思路。
仅由一个内容仅仅两行左右的函数以及一个数组就可以实现。
优点:方便快捷,使用有效。
缺点: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  );      
	*/
			}
	  }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值