「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」
小明的亲戚圈
小明家是一个大家族,大到很多亲戚都互不相识;但是小明是程序员,想做一个自己的“亲戚圈”,输入一个人可以立即知道此人是否是自己的亲戚。
小明的亲戚圈是这样定义的:小明与A是亲戚,A与C是亲戚,B与C是亲戚;小明与B也是亲戚;给以小明身边的的亲戚关系,如何判断一个人跟小明是亲戚;
小明与A是亲戚,A与C是亲戚,B与C是亲戚;小明与B也是亲戚;
初始时,小明只与自己时亲戚,A、B、C也是毫无关系的。
graph LR
小明 --> 小明
A --> A
B --> B
C --> C
示例中小明与A是亲戚,此时在图中将A指向小明,为什么是A指向小明而不是小明指向A;这个不影响,后面讨论。跟着思路来
graph LR
小明 --> 小明
A --> 小明
B --> B
C --> C
此时A指向小明,表示A的亲戚链有小明,从小明出发也能找到亲戚A;
示例中又说A与C是亲戚
此时A已经与小明有亲戚关系了,如何让C添加进这个亲戚关系呢?
此时就是并查集中的核心,并的部分了
如合并,通过图可以看到,C指向C说明暂时只与自己有亲戚关系,A确不是指向自己,A指向小明;这是把C直接指向小明是不是可以将C添加到A-小明的‘亲戚圈’了。暂时先这么处理,看看后续有没有什么问题
graph LR
小明 --> 小明
A --> 小明
B --> B
C --> 小明
在看示例说明B与C是亲戚;
B如何加入A-小明-C这个“亲戚圈”呢?老套路
- B指向B说明暂时只与自己有亲戚关系
- C指向的是小明,将B指向小明即可
graph LR
小明 --> 小明
A --> 小明
B --> 小明
C --> 小明
此时输入A与B,A递归向上找自己的亲戚关系,B递归向上找自己的亲戚关系;只到找到某个只指向自己的人,就是自己亲戚关系的根节点。这个图中A与B共同指向小明,说明A与B是亲戚关系;
上述解决问题的思想就叫并查集。
朋友圈问题
有一批人,已知他们的关系,求朋友圈的数量。 刚开始谁也不认识谁,每个人都是相互独立的,自己是自己朋友圈的代表,这点可以理解吧
这时有朋友圈9个朋友圈
graph LR
0 --> 0
1 --> 1
2 --> 2
3 --> 3
4 --> 4
5 --> 5
6 --> 6
7 --> 7
8 --> 8
首先,0与1成为好朋友,5与6成为好朋友;2与3成为好朋友 将他们的关系修改为下图所示
现在0是0、1这个朋友圈的代表人物;5是5、6这个朋友圈的代表人物,2是2、3这个朋友圈的代表人物
这时有6个朋友圈
graph LR
0 --> 0
1 --> 0
2 --> 2
3 --> 2
4 --> 4
5 --> 5
6 --> 5
7 --> 7
8 --> 8
然后3与6成为好朋友了。
- 3对6说:我的代表是2,咱们成为了好朋友,你要不要也认2作为代表?
- 6对3说:我看我是不是代表,然后6弱弱的说,我的代表是5,我自己做不了主;
- 3对6说:咱咋弄?
- 3对6说:让2跟5去处理
- 然后2与5一番交谈,2当代表 然后2就成了5,6,3朋友圈的代表
疑问1
6是如何找自己的代表的?
//
function find(n) {
//自己是代表吗?
if (list[n] !== n) {
//不是,向上找自己的代表
list[n] = find(list[n])
}
// 找到了,返回这个代表
return list[n]
}
疑问2
3和6是如何2和5去处理的
function marge(i, j) {
const x = find(i)
const y = find(j)
list[x] = y
}
这时有5个朋友圈
graph LR
0 --> 0
1 --> 0
2 --> 2
3 --> 2
4 --> 4
5 --> 2
6 --> 5
7 --> 7
8 --> 8
然后1与7成为朋友
- 1对7说:我的代表是0,咱们成为了好朋友,你要不要也认0作为代表?
- 7对1说:我看我是不是代表,然后7说我是代表我可以认0作为代表;
这时有4个朋友圈
graph LR
0 --> 0
1 --> 0
2 --> 2
3 --> 2
4 --> 4
5 --> 2
6 --> 5
7 --> 0
8 --> 8
现在看一下leetcode省份问题
省份数量
城市之间的关系数组 isConnected = [[1,1,0],[1,1,0],[0,0,1]]
初始时,每个城市都是相互独立的,每个城市都表示一个省
通过枚举城市之间的关系数组isConnected,当i === j 时跳过
graph LR
0 --> 0
1 --> 1
2 --> 2
i=0,j=0;跳过,i=0,j = 1;此时 isConnected[i][j] === 1 ,说明城市0与城市1是彼此相连
图中可以看到,城市0与城市1都是指向自己的城市,所以可以任选一个作为代表本省的城市。假设城市1作为本省城市,将城市0指向1
graph LR
0 --> 1
1 --> 1
2 --> 2
i=1,j = 0;此时 isConnected[i][j] === 1 ,说明城市1与城市0是彼此相连;
途中看到城市1已经表示一个省了,城市0指向城市1,说明城市0与城市1已经在一个省;
最后如何统计省份的数量呢?
只要不是指向自己的,统计省份的时候忽略,只统计指向自己的城市就表示省份数量。
为什么?在这个示例中,因为城市0与城市1彼此相连;城市0让城市1代表自己了
graph LR
0 --> 1
1 --> 1
2 --> 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
}
}