题目
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
示例
输入: isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出: 2
题解
并查集
不要问为啥用并查集,因为作者暂时只会用并查集解决这个问题;当然大神可以用DFS和BFS搞定,我不太会,所以此处只讨论使用并查集解决这个问题
什么是并查集
并查集就是将相同类型合并,查询两个已知量是否在一个集合;
合并
将相同类型合并,以本题为例,城市1和城市2相连,得到1和2有关系;将1和2合并;
重点
怎么合并?
以本题为例,isConnected = [[1,1,0],[1,1,0],[0,0,1]]
- 构建数组list = [0,1,2];这个数组下标表示城市,元素表示与下标城市相连的其他城市。
- 数组list = [0,1,2]起始时城市0与城市0相连,城市1与城市1相连,城市2与城市2相连;这个可以理解吧
- i = 0 , j = 0 ,为什么return;因为i = j时,i和j表示的时一座城市,不需要合并。
- 枚举isConnected,当i = 0 , j = 1时,需要将城市0与城市1连接,将list[0] = list[1];得到list = [1,1,2]。
- 此时的list = [1,1,2]表示城市0与城市1相连,城市1与城市1相连,城市2与城市2相连;
- 继续枚举,当i = 1, j = 0时,需要将城市1与城市0连接,这里如何合并呢?
- j = 0,list[j] = list[0] = 1;这里list[0] = 1表示,城市0与城市1连接,所以继续list[1]与那个城市相连,list[1] = 1,说明list[1]是根节点,不需要再向上寻找
- i = 1 , list[i] = list[1] = 1;城市1是根节点,不需要再向上寻找
- list = [1,1,2]
- 枚举结束
- 枚举list,对于任意元素k只统计list[k] = k的数量即可得到省份数量
- 比如list = [1,1,2],只有第2位,第3位list[k] = k
- 所以isConnected = [[1,1,0],[1,1,0],[0,0,1]]省份为2;
代码
class UnionFind {
constructor(n) {
this.node = new Array(n).fill().map((item, idx) => idx)
}
// 查找当前元素所在的根节点
find(x) {
if (x === this.node[x]) {
return x
}
return this.find(this.node[x])
}
// 合并x,y所处集合
merge(x, y) {
x = this.find(x)
y = this.find(y)
if (x === y) return
this.node[x] = y
}
}
var findCircleNum = function (isConnected) {
const len = isConnected.length
const unionFind = new UnionFind(len)
for (let i = 0; i < len; i++) {
for (let j = 0; j < len; j++) {
if (isConnected[i][j] === 1) {
unionFind.merge(i, j)
}
}
}
let result = 0
for (let i = 0; i < unionFind.node.length; i++) {
if (unionFind.node[i] === i) result++
}
return result
}
// 0,1,2,3,4,5,6,7,8
var isConnected = [
[1, 1, 0],
[1, 1, 0],
[0, 0, 1],
]
const aa = findCircleNum(isConnected)
console.log(aa)