Java算法——并查集

功能

并查集是管理多个集合的算法,其功能包括集合的合、集合内或集合间的询。

背景

在中国古代,人们都比较重视血缘传承,几乎每家都至少会有一个男孩,后人总会牢记自己的祖先,这样就有了家谱 / 族谱。根据家谱 / 族谱,可以将后面几代人通过祖先的祖先都联系起来,使大家千万年前都是一家人。

现在有一个问题,如果每个人都只记得自己的爹,那么如何快速判断任意两个人,是否来自同一祖先呢?

输入示例

第一行两个整数 n m ,家谱共有n个人,编号从1 ~ n
接下来m行,每行俩数i j,i 是 j 的儿子,i, j ∈ [1,n]
最后一行,俩数 a b ,问a、b是否来自同一祖先

7 6
3 1
6 2
7 3
5 2
4 1
2 1
6 7

输出示例

如果a、b来自同一祖先,输出true,否则输出false

true

分析

  • 每个爹可能有不止一个儿子,也可能一个儿子也没有
  • 顶级祖先不再有爹

家谱

我们用上面树(有向图)的形式直观表示一个家谱,如果想要判断 6 号和 7 号是否为一家人,那么就要先从一方起,逐步向上追查,一直追溯到顶级祖先,记为 x,然后再追查另一个人的顶级祖先,记为 y,如果x == y,二者为一家人。

如果直接构建树(有向图)恐怕有些复杂,你得先知道祖先是谁,然后从祖先出发,寻找与祖先直接相关的儿子,然后再分别以这些儿子为起点,再次搜索。单单是构建的过程花销就很大,因此不推荐。

下面采用并查集的方式解决此类问题。

方式一

直链式 “并”查集。

首先开辟一个n+1长度的数组,用于容纳 1 ~ n号人,然后以儿子的编号作为数组索引,将目标位置修改为父亲的编号。
例如输入数据 3 1,将 3 号索引的位置改成1,代表 3 号是 1 号的儿子
直链式并查集

private static int[] people;

public static void main(String[] args){
	Scanner input = new Scanner(System.in);
	int n = input.nextInt();
	int m = input.nextInt();
	people = new int[n + 1];
	//首先将自己的爹设为自己,因为读取数据前,每个人都有可能是顶级祖先
	for(int i = 1; i <= n; i++) { people[i] = i; }
	//开始读取父子关系数据
	while(m-- > 0){
		int son = input.nextInt();
		int father = input.nextInt();
		//认爹
		people[son] = father;
	}
	int a = input.nextInt();
	int b = input.nextInt();
	//如果a、b的顶级祖先是一个人,他俩就是一家人
	System.out.println( getAncestor(a) == getAncestor(b) );
}

//找到 p 的顶级祖先
private static int getAncestor(int p){
	//如果我爹是自己,那么我就是顶级祖先
	if(people[p] == p){
		return p;
	}else{
		//否则问我爹,咱祖先是谁
		return getAncestor(people[p]);
	}
}

方式二

归一式并查集。

如果有多组查询,方式一这种逐级上问的效率就会很低,可能会有大量的重复询问。

所以方式二做个优化,如果一个人已经知道了自己的顶级祖先,就可以让自己直接记住祖先是谁就好了,下次再问我,我直接就能给出答案。

路径压缩

private static int getAncestor(int p){
	//如果我爹是自己,那么我就是顶级祖先
	if(people[p] == p){
		return p;
	}else{
		//否则问我爹,咱祖先是谁
		//增加回溯,我爹打听到的祖先,我也记下来
		return people[p] = getAncestor(people[p]);
	}
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值