算法学习--Union-Find算法

Union-Find算法有它的实际用途。多用于动态连通的应用场景。


Union-Find算法是给出两个节点,判断它们是否连通,如果连通,是不需要给出具体的路径的


举两个例子作为主要表现:

1、在网路连接中,当发现没有连接的两个节点,可以把他们连接起来,一旦节点都连接起来,又能把多余的线拆除,这时候可以采用Union-Find算法思想;

2、参考编译器在编译一个变量时,额外声明多个指针指向同一变量,这时候,能获知哪些指针实际的指向,也是应用Union-Find思想的时候。


思考路线:

1、首先,构建数组储存编号id [ N ] ,确定一共有多少节点 (N);我们通常选择树的结构储存像这样的数据,树的结构有利于

2、编写Find函数,Find函数旨在告诉我们,Find(int p)里面的p属于那一个 id[P];

3、编写Union函数,Union函数作用是连结两个节点Union(int a,int  b)而通常,两个节点都有属于自己的Group。

4、之后我们开始优化,Find函数和Union函数;

5、Find函数,参考(Quick-Union 算法),让Find 函数增加判断,让所寻找的值等于根节点 id[ p ],因为在树的连接时,id号变更了 ;参考(路径压缩的Weighted Quick-Union算法)我们把它进行改造让它实现find时,返回的值与树的根部接近且为1;

6、Union函数,有两步改造参考(Quick-Union 算法)在两个节点联合的时候,把两棵树的其中一棵变为另外一棵树的子树;第二步,参考(Weighted Quick-Union算法),增加两棵树大小的判断,引入树的大小参量。


下面是代码:

#include <stdio.h>

class UF  
{  
public:
	int *id; // access to component id (site indexed)  
	int count; // number of components 

	//最原始的Quick-Find
	UF(int N)  
	{  
		// Initialize component id array.  
		count = N;  
		id = new int[N];  
		for (int i = 0; i < N; i++)  
			id[i] = i;  
	}  

	int Count()  
	{ 
		return count;
	}  

	bool connected(int p, int q)  
	{ 
		return find(p) == find(q);
	}  

	int find(int p)  
	{ 
		return id[p]; 
	}  

	void Union(int p, int q)  
	{   
		// 获得p和q的组号  
		int pID = find(p);  
		int qID = find(q);  
		// 如果两个组号相等,直接返回  
		if (pID == qID) return;  
		// 遍历一次,改变组号使他们属于一个组  
		for (int i = 0; i < (sizeof(id)/sizeof(int)); i++)  
			if (id[i] == pID) id[i] = qID;  
		count--;  
	}//不好的地方是需要全部遍历
	
	//下面是各种改良

	//Quick-Union
	int find(int p)  
	{   
		// 寻找p节点所在组的根节点,根节点具有性质id[root] = root  
		while (p != id[p]) p = id[p];  
		return p;  
	}  
	void Union(int p, int q)  
	{   
		// Give p and q the same root.  
		int pRoot = find(p);  
		int qRoot = find(q);  
		if (pRoot == qRoot)   
			return;  
		id[pRoot] = qRoot;    // 将一颗树(即一个组)变成另外一课树(即一个组)的子树  
		count--;  
	} 

	//Weighted Quick-Union
	int *sz;//增加SZ作为树的大小
	UF(int N)  
	{  
		// Initialize component id array.  
		count = N;  
		id = new int[N];  
		for (int i = 0; i < N; i++)  
			id[i] = i;  
		for (int i = 0; i < N; i++)  
			sz[i] = 1;    // 初始情况下,每个组的大小都是1 
	}  
	void Union(int p, int q)  
	{  
		int i = find(p);  
		int j = find(q);  
		if (i == j) return;  
		// 将小树作为大树的子树  
		if (sz[i] < sz[j])
		{ 
			id[i] = j; sz[j] += sz[i];
		}  
		else 
		{ 
			id[j] = i; sz[i] += sz[j];
		}  
		count--;  
	}  


	//路径压缩的Weighted Quick-Union算法,让树更扁平化
	int find(int p)  
	{  
		while (p != id[p])  
		{  
			// 将p节点的父节点设置为它的爷爷节点  
			id[p] = id[id[p]];  
			p = id[p];  
		}  
		return p;  
	}  
};



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值