现在的位置: 首页 > 综合 > 正文

关于不相交集

2013年04月05日 ⁄ 综合 ⁄ 共 1924字 ⁄ 字号 评论关闭

 参考: 数据结构与算法分析——Java语言描述  (Mark Allen Weis 

       
不相交集
,这种数据结构以前我倒是听都没听过。它是解决等价问题的一种有效数据结构。这种数据结构实现起来很简单,每个例程只需要几行代码,而且可以使用一个简单的数组。它的实现也非常快,每种操作只需要常数平均时间。

        给定一个等价关系“~”,一个自然的问题是对于任意的a 和 b,确定是否 a~b 。元素a∈S的等价类是S的子集,它包含所有与a有等价关系的元素。注意,等价类形成对S的一个划分:S的每一个成员恰好出现在一个等价类中。为确定是否a~b,我们只需验证a 和b是否都在同一个等价类中。

        有两种操作允许进行。第一种操作是find,它返回包含给定元素的集合(即等价类)的名字。第二种操作是添加关系。如果我们要添加关系a~b,那么首先要看是否a和b 已经有关系。这可以通过对a和b执行find并检验它们是否在同一个等价类中来完成。如果它们不在同一个类中,那么使用求并操作union,这种操作把含有a和b两个等价类合并成一个新的等价类。

        记住,我们的问题不要求find操作返回任何特定的名字,而只是要求当且仅当两个元素属于同一个集合时,作用在这两个元素上的find返回相同的名称。一种想法是可以用树来表示每一个集合,因为树上的每一个元素都有相同的根,这样,该根就可以用来命名所在的集合。我们用数组来实现。数组中的每一个成员S[i]表示元素i的父亲,如果i是根,那么S[i]=-1。

        为了执行两个集合的union操作,我们通过使一棵树的根的父链链接到另一棵树的根节点合并两棵树。我们在这里约定,在union(x,y)之后,新的根是x。对元素x的一次find(x)操作通过返回包含x的根完成。下面这幅图演示了union操作的过程:

        这一数据结构的实现代码如下:       

package com.test;

public class DisjSets {
	
	private int[] s;
	
	public DisjSets(int numElements)
	{
		s=new int[numElements];
		for(int i=0;i<s.length;i++)
			s[i]=-1;
	}
         /**
	 * @param root1  the root of set1
	 * @param root2  the root of set2
	 */
	public void union(int root1,int root2)
	{
		s[root2]=root1;
	}
	/**
	 * 
	 * @param x the element being searched for
	 * @return the set containing x
	 */
        public int find(int x)
	{
		if(s[x]<0)
		    return x;
		else
	             return find(s[x]);			
	}
}

        可以看到,它的实现很简单。然而,目前为止,这种union的操作并不是最好的,因为我们规定union(x,y)后新的根是x,这一规则太过简单,很容易就会造成很深的树,这样在执行find操作的时候代价太大。一种改进的算法是按高度求并,它保证所有的树的深度最多是O(logN)。我们跟踪每棵树的高度执行union操作使得浅的树称为深的树的子树。由于零的高度不是负的,所以在根节点我们实际上存储的高度的负值减去附加的1,初始时所有项还是-1。对find(x)操作例程也要做一些修改,这种修改的操作叫做“路径压缩”,它的效果是从x到根的路径上的每一个节点都使它的父节点变成跟。修改后的代码如下:

package com.test;

public class DisjSets {
	
	private int[] s;
	
	public DisjSets(int numElements)
	{
		s=new int[numElements];
		for(int i=0;i<s.length;i++)
			s[i]=-1;
	}
	/**
	 * @param root1  the root of set1
	 * @param root2  the root of set2
	 */
	public void union(int root1,int root2)
	{
		if(s[root2]<s[root1])
			s[root1]=root2;
		else{
			if(s[root1]==s[root2])
				s[root1]--;
			s[root2]=root1;
			}
	}
	/**
	 * 
	 * @param x the element being searched for
	 * @return the set containing x
	 */
	public int find(int x)
	{
		if(s[x]<0)
			return x;
		else
			return s[x]=find(s[x]);			
	}
}

抱歉!评论已关闭.