Union-find(Algorithms, Part 1, week 1 )

最近开始在Coursera上听公开课, Princeton的算法课感觉挺不错的,鉴于自己原来欠下了太多技术债务,决定好好努力,争取能够跟下来,每堂课会总结记录一点东西

课程视频的地址:https://class.coursera.org/algs4partI-006/lecture, 感谢Coursera提供了这么好的一个平台

中文相关资料:http://blog.csdn.net/dm_vincent/article/details/7655764


union-find算法翻译成中文有的叫并查集/联合查找算法,主要实现判断两个节点之间是否有通路,

如下图所示union(4,3)在4和3之间建立连接,connected(0,7)判断0和7之间是否由有连接

连接具有对称性和传递性

(1)p->q, 则q->p

 (2) p->q,q->r,则p->r

《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

uion过程举例

《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

Union-Find的API接口

《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

1.Quick-Find

设元素个数为N,利用id[N]数组存储连接情况,如下图所示若第i个元素与第j个元素相连接,则id[i]=id[j]
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

public class QuickFindUF
{
	
	private int id[];
	
	//初始化 id数组
	public QuickFindUF(int N)
	{
		id=new int[N];
		for(int i=0;i<N;i++)
		{
			id[i]=i;
		}
	}
	
	public boolean Connect(int p,int q)
	{
		return (id[p]==id[q]);
	}
	
	//将所有id[]中与pid相同的置为qid
	public void Union(int p,int q)
	{
		int pid=id[p];
		int qid=id[q];
		
		if(pid!=qid)
		{
			for(int i=0;i<id.length;i++)
			{
				if(id[i]==pid)
				{
					id[i]=qid;
				}
			}
		}
	}	

}



这种方法的一个Union命令的执行需要访问id[]数组N次,过于复杂

2.Quick-Union

《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

当一个元素i,id[i]=i时,i为root
利用树的结构,每次union(p,q)的时候只将p所在的组的root的值改为q所在组的root的值
判断connect时,若root(p)==root(q),则认为两者是连通的

public class QuickUnionUF
{
	private int id[];
	
	//初始化 id数组
	public QuickUnionUF(int N)
	{
		id=new int[N];
		for(int i=0;i<N;i++)
		{
			id[i]=i;
		}
	}
	
	//返回元素i的root节点
	private int root(int p)
	{
		while(id[p]!=p)
		{
			p=id[p];
		}
		return p;
	}
	public boolean Connect(int p,int q)
	{
		return (root(p)==root(q));
	}
	
	//修改p所在组的root,将id[root(p)]=root(q)
	public void Union(int p,int q)
	{
		int i=root(p);
		int j=root(q);
		id[i]=j;				
	}	
}

3.Improvement

QuickUnion方法建立的树可能会很高,最坏情况是一个链表状的,这样每次在判断connect,寻找root的时候就会比较耗时
所以想建立尽量平衡的树,所有有以下改进

(1)Improvement 1:weighting

《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

QuickUnion方法不管p和q所在树的大小直接把p所在的root挂在q的root上
通过weight方法判断p和q的所在树的大小,将较小的树挂在较大的树上,这样会实现尽量的平衡
为了记录树的大小引入sz[]数组,sz[i]表示以i为root的树的大小(即有多少个元素),在union函数中修改sz[]
《Union-find(Algorithms, Part 1, week 1 )》



《Union-find(Algorithms, Part 1, week 1 )》

任何一个元素x的深度不会大于lgN,lg表示以2为底取对数(因为树最坏的情况是二叉的,每层元素少,所以深度会较深)


《Union-find(Algorithms, Part 1, week 1 )》

(2)Improvement 2:path compression

 
在寻找节点p的root后,将经过的路径中的每一个节点都直接挂在root下
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

有两种实现方法
1. 在root函数中直接在找root的while循环后再加一层循环,将p到root途经的每一个节点都直接指向root
2.直接在root的while循环中修改,将其指向它的grandpa节点~(不如第一种方法彻底,但是也会压缩路径)
《Union-find(Algorithms, Part 1, week 1 )》



M次操作的复杂度:
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

lg*运算代表迭代取lg运算 while(lgN>1){ N=lgN; i++}  e.g. lg*65536=5

4.应用

连通性的估计
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》



从上到下的渗透,白色的格子表示连通区域,白色的格子越多,从上到下渗透的可能性越大
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

求这样一个白色格子所占比例p的临界值,当大于p时有很大的可能性连通




利用蒙特卡罗估计方法,现将N*N的格子全部置黑,然后每一次随机置白一个格子(union过程),计算从最顶层元素到最低层元素是否连通(判断connect过程)
计算过程中,不用依次计算每一个顶层格子到底层格子是否连通,直接将所有的顶层连接到一个虚拟的root,将底层连接到另一虚拟的root上,每次只判断两个root是否连通即可,通过多次反复试验,估计p值
《Union-find(Algorithms, Part 1, week 1 )》

《Union-find(Algorithms, Part 1, week 1 )》

点赞