刷题日志--并查集 6.3
省份数量
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/bLyHh0
问题叙述
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
示例 1:
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
提示:
1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]
分析
并查集的简单应用。
并查集是一种树型的数据结构,主要用于解决以下两个问题
- 将两个集合合并
- 询问两个元素是否在同一个集合当中
基本原理:每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,parent[x]表示x的父节点。
对于每一个点,我们都存储一下它的父节点是谁,当我们相求某一个点属于哪个集合的时候,我们就求其父节点是谁,如果父节点不是树根,那么继续往上找,直到找到树根,因此我们可以用这种方式快速求出某个元素是属于哪个集合的。
问题1:如何判断树根?
- if(parent[x] == x)
问题2:如何求x的集合编号?
- while(parent[x] != x) x = parent[x];
问题3:如何合并两个集合?
- 把一棵树,嫁接到另一棵树,成为另一棵树的儿子。p[x]是x的编号,p[y]是y的编号。p[x] = y;
根据题中的定义:省份是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给我们的二维数组isConnected[i][j]表示:第i个城市和第j个城市相连。
我们用并查集,将所有的相连的城市合并,最终的祖宗节点个数,就是省份个数
Code
1 | class Solution { |
冗余连接
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/redundant-connection
问题叙述
树可以看成是一个连通且 无环 的 无向 图。
给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。
示例 1:
输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]
示例 2:
输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
提示:
- n == edges.length
- 3 <= n <= 1000
- edges[i].length == 2
- 1 <= ai < bi <= edges.length
- ai != bi
- edges 中无重复元素
- 给定的图是连通的
分析
通过并查集来寻找冗余的边。初始时,每个节点都属于不同的集合,遍历每一条边,判断这条边的两个顶点是否在同一个集合中。
- 如果两个顶点不在一个集合中,说明加上这条边,不会出现环,那我们就直接把这两个集合合并
- 如果两个顶点在一个集合中,那我们再加上这条边,必然会导致环的出现,为冗余的边,所以将当前边作为结果返回即可
Code
1 | class Solution { |
【模板】并查集
来源:洛谷
链接:https://www.luogu.com.cn/problem/P3367
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 $N,M$ ,表示共有 $N$ 个元素和 $M$ 个操作。
接下来 $M$ 行,每行包含三个整数 $Z_i,X_i,Y_i$ 。
当 $Z_i=1$ 时,将 $X_i$ 与 $Y_i$ 所在的集合合并。
当 $Z_i=2$ 时,输出 $X_i$ 与 $Y_i$ 是否在同一集合内,是的输出
Y
;否则输出 N
。
输出格式
对于每一个 $Z_i=2$ 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
样例 #1
样例输入 #1
1 | 4 7 |
样例输出 #1
1 | N |
提示
对于 $30%$ 的数据,$N \le 10$,$M \le 20$。
对于 $70%$ 的数据,$N \le 100$,$M \le 10^3$。
对于 $100%$ 的数据,$1\le N \le 10^4$,$1\le M \le 2\times 10^5$,$1 \le X_i, Y_i \le N$,$Z_i \in { 1, 2 }$。
分析
就当练练手了
Code
1 | import java.util.Scanner; |
亲戚
来源:洛谷
链接:https://www.luogu.com.cn/problem/P1551
题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定:$x$ 和 $y$ 是亲戚,$y$ 和 $z$ 是亲戚,那么 $x$ 和 $z$ 也是亲戚。如果 $x$,$y$ 是亲戚,那么 $x$ 的亲戚都是 $y$ 的亲戚,$y$ 的亲戚也都是 $x$ 的亲戚。
输入格式
第一行:三个整数 $n,m,p$,($n,m,p \le 5000$),分别表示有 $n$ 个人,$m$ 个亲戚关系,询问 $p$ 对亲戚关系。
以下 $m$ 行:每行两个数 $M_i$,$M_j$,$1 \le M_i,~M_j\le N$,表示 $M_i$ 和 $M_j$ 具有亲戚关系。
接下来 $p$ 行:每行两个数 $P_i,P_j$,询问 $P_i$ 和 $P_j$ 是否具有亲戚关系。
输出格式
$p$ 行,每行一个 Yes
或 No
。表示第 $i$ 个询问的答案为“具有”或“不具有”亲戚关系。
样例 #1
样例输入 #1
1 | 6 5 3 |
样例输出 #1
1 | Yes |
分析
依旧是一道练手题
Code
1 | import java.util.Scanner; |