连通性问题

一、慢速并查集

对于每个并集运算,要搜索所有的对象,所以速度较慢

由下图可以看出,就是把所有的相同的集合都赋值为一样,就表示相同的集合;每个对象只有一个父节点

#include<stdio.h>
#define N 10000
int main()
{
	int i,p,q,t,id[N];
	for(i = 0;i < N;i++)
		id[i] = i;
	while(scanf("%d %d",&p,&q) == 2)
	{
		if(id[p] == id[q])  continue;
		else
		{
			t = id[p];
			for(i = 0;i < N;i++)
			{
				if(id[i] == t)
					id[i] = id[q];
			}
		}
		printf("%d %d\n",id[p],id[q]);
	}
	return 0;
}

性质1-1 对于N个对象,每个对象包括M个并集运算的连通性问题,使用快速查找(慢速并集)算法至少要执行MN条指令,因为对于M个并集运算,每个运算要迭代for循环N次

二、快速并查集

通过指针的引用实现快速查找;并集的运算计算量少(只用一次),查找运算计算量大(要不断的搜索父节点)

#include<stdio.h>
#define N 10000
int main()
{
	int i,j,p,q,t,id[N];
	for(i = 0;i < N;i++)
		id[i] = i;
	while(scanf("%d %d",&p,&q) == 2)
	{
		for(i = p;i != id[i]; i = id[i]);//寻找左边的父节点
		for(j = q;j != id[j]; j = id[j]);//寻找右边的父节点
		if(i == j)
			continue;
		id[i] = j;
		printf("%d %d\n",id[p],id[q]);
	}
	return 0;
}

性质1-2  若M>N 快速并集算法可能要运行多于M*N/2条指令(主要是平均查找高度)解决一个拥有N个对象、M个对的连通性问题


三、加权快速并集运算(解决1-2的不利事件)

//将树的高度降低,连接两棵树中较小树的根与较大树的根的结果,每个节点与树根的距离短,所以查找效率高

性质1-3 加权快速并集算法最多只要跟踪2*lgN个指针


所以增加一个数组保存节点数,防止树中长路径的增长。代码为:

#include<stdio.h>
#define N 10000
int main()
{
	int i,j,p,q,t,id[N];
	int sz[N];//记录
	for(i = 0;i < N;i++)
	{
		id[i] = i;
		sz[i] = 1;
	}
	while(scanf("%d %d",&p,&q) == 2)
	{
		for(i = p;i != id[i]; i = id[i]);//寻找左边的父节点
		for(j = q;j != id[j]; j = id[j]);//寻找右边的父节点
		if(i == j)
			continue;
		if(sz[i] < sz[j])
		{
			//小根连接大根 这样子就不容易增长
			id[i] = j;
			//只需要关注最终集合即可,原来的集合已经被包含了
			sz[j] += sz[i];
		}
		else
		{
			id[j] = i;
			sz[i] += sz[j];
		}
		printf("%d %d\n",p,q);
	}
}

问题:能否确保具有线性性能的算法?这是一个难以回答的问题

但是有很多容易的方法可以进一步改进加权并集算法,理想情况下,希望每个节点直接指向树的根节点

可以采用路径压缩!

路径压缩:在并集运算中,添加另一遍次经过每条路径,将路径中对应每个顶点的ID项设置成指向根。最终结果是几乎把树压平。


四:对分路径压缩

#include<stdio.h>
#define N 10000
int main()
{
	int i,j,p,q,t,id[N];
	int sz[N];//记录
	for(i = 0;i < N;i++)
	{
		id[i] = i;
		sz[i] = 1;
	}
	while(scanf("%d %d",&p,&q) == 2)
	{
		for(i = p;i != id[i]; i = id[i])//寻找左边的父节点
			id[i] = id[id[i]];
		for(j = q;j != id[j]; j = id[j]);//寻找右边的父节点
			id[j] = id[id[j]];
		if(i == j)
			continue;
		if(sz[i] < sz[j])
		{
			//小根连接大根 这样子就不容易增长
			id[i] = j;
			//只需要关注最终集合即可,原来的集合已经被包含了
			sz[j] += sz[i];
		}
		else
		{
			id[j] = i;
			sz[i] += sz[j];
		}
		printf("%d %d\n",p,q);
	}
}


几种方法的对比:(其中方法4在练习中)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值