不相交集类算法

1、不相交集类的算法:

这是处理等价问题的一种算法,这里的等价指的是集合,a与b等价,是指a∈S,b∈S。通过不相交集类的两个基本操作find就可找出a和b的集合标志是否一致,若一致则说明它们是一个集合的。当然,对于集合的表示,用的是森林的数据结构。集合的标志就是根节点,若根节点一样,就说明集合一样。而除了唯一的根节点之外,剩下的节点里面存放都是父节点的标志。因此,我们只需要一个数据就能表示一个不相交集的结构了。不相交集类的另一个操作就是union。意图是把两个不属于同一集合的元素,进行合并,使其等价




在union(int root1,int root2)操作中,合并的方式是有三种:

第一种是按照root1为根,root2为子节点的方式,也就是如果root1和root2合并的话,就需要把root2中原本存放的集合标志,换成指向root1.

第二种是集合中节点数个数的大小来判断谁合并谁;若root1的个数大于root2的个数,则root1为根,root2为节点;

第三种是集合中树的高度的大小来判断谁合并谁;若root1的高度大于root2的高度,则root1为根,root2为节点;

第二、三种之所以这么做的原因是,按照第一种的方法,最终导致的是树变得太深,每次的find操作消耗的时间长。通过控制个数或者高度,可以有效的减缓树变得越来越深。当然,最终树的深度依旧会变大。尝试着在find操作进行优化


find路径压缩算法:

每次,在find(int x)找到跟节点之后,就将这个x添加到这个根节点的下面。那么,下次寻找x的根的时候,其实,只要进行一次搜索即可,也就意味这递归的次数将大大减少。这是牺牲了树的广度来减小树的深度。事实上,这种方法很有效,因为,我们的不相交集类是通过一个一维数组来储存的,广度的大小根本就不需要在意




import java.util.ArrayList;

/**
 * 不相交集类
 * 
 * @author Holy-Spirit
 * 
 */

public class DisjSet {

	private int length;
	private int[] array;

	/**
	 * 初始化数组为-1
	 * 
	 * 其中符号,代表的是这是根节点,也就是表示这个下标是这棵树的标志 符号后面的值为这棵树节点的个数,或者是高度值
	 * 
	 * 如果大于0,说明这是树的子节点,里面存放的是父节点的下标
	 * 
	 * @param length
	 *            数组的长度
	 */

	public DisjSet(int length) {

		if (length <= 0) {
			System.out.println("数组初始化长度有误");
			return;
		}
		array = new int[length];
		this.length = array.length;
		for (int i = 0; i < array.length; i++) {
			array[i] = -1;

		}

	}

	/**
	 * 获取一个下标所在的整个集合的值
	 * 
	 * @param index
	 *            下标值
	 * @return 整个集合的List
	 */

	public ArrayList<Integer> getDisjMember(int index) {
		int count = array[index] < 0 ? (array[index] * -1)
				: (array[find(index)] * -1);
		ArrayList<Integer> list = new ArrayList<>();
		list.add(index);
		findAll(list, index, count);
		return list;
	}

	/**
	 * @param list
	 *            需要返回的List对象
	 * @param parentRoot
	 *            父节点
	 * @param count
	 *            这个集合的节点个数
	 * @return 集合List
	 */
	private ArrayList<Integer> findAll(ArrayList<Integer> list, int parentRoot,
			int count) {

		if (count == 0) {
			return list;
		}

		for (int i = 0; i < array.length; i++) {
			if (array[i] == parentRoot) {
				list.add(i);
				findAll(list, i, count--);
			}
		}

		return list;
	}

	/**
	 * 获取这个数组长度
	 * 
	 * @return
	 */

	public int length() {

		return this.length;
	}

	/**
	 * 判断是否全部合并
	 * 
	 * @return
	 */

	public boolean isUnionAll() {

		boolean isUnionAll = false;

		for (int i = 0; i < array.length; i++) {
			if (array[i] < 0) {

				if (isUnionAll) {
					isUnionAll = !isUnionAll;
					break;
				}
				isUnionAll = !isUnionAll;

			}

		}

		return isUnionAll;
	}

	/**
	 * 通过比较两个集合的个数来决定谁被归并 每次归并,在根节点,存放整个集合中节点的个数
	 * 
	 * @param root1
	 * @param root2
	 */

	public void unionBySzie(int root1, int root2) {

		int sumSize = array[root1] + array[root2];

		if (array[root1] > array[root2]) {
			array[root2] = sumSize;
			array[root1] = root2;
		} else {
			array[root1] = sumSize;
			array[root2] = root1;
		}

	}

	/**
	 * 通过集合的高度进行归并
	 * 
	 * 高度小的集合称为高度大的树的子节点
	 * 
	 * @param root1
	 * @param root2
	 */

	public void unionByHeigh(int root1, int root2) {

		if (array[root1] < array[root2]) {
			array[root1] = root2;
		} else {
			if (array[root1] == array[root2]) {
				array[root1]--;
			}
			array[root2] = root1;

		}

	}

	/**
	 * 路径压缩的查找方式 通过根的下标来标识树集合
	 * 
	 * 路径压缩的思想是,union的最后或多或少会破坏一棵树的平衡性 所以每次
	 * find后,都将这个find的位置中存放的父节点的下标值改成根节点的下标,这样一来,树的高度将变小。但是
	 * 
	 * @param x
	 * @return 最终返回的是这个集合的根节点,因为根节点中存放着这棵树的大小或者高度
	 */

	public int find(int x) {
		if (array[x] < 0) {
			return x;
		} else {
			return array[x] = find(array[x]);
		}
	}

}


不相交集类的思想在很多地方都有用处,比如要产生一个迷宫...







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值