
一、并查集
1.1 并查集
只要可以连通则可组成集合, 集合内可只剩一个石头, 并查集的集合数就是最终石头的个数
因为只要能 连通的石头, 可只剩余一个, 即从边缘开始消除即可
即一个集合只剩一个石头, 所以返回并查集的数量即可
首先, 每个石头编号 0, 1, …, n-1
初始时, 每个石头单独成为一个集合
然后, 用两个哈希表, 行哈希表记录该行第一次出现的石头编号, 列哈希表记录该列第一次出现的石头编号
然后, 遍历每个石头,
- 若该石头的横坐标是第一次出现, 则加入行哈希表; 否则 将该石头编号 和 改行第一次出现的石头编号 这两个集合合并
- 若该石头的纵坐标是第一次出现, 则加入行哈希表; 否则 将该石头编号 和 改列第一次出现的石头编号 这两个集合合并
最终, 集合的数量, 就是最终剩余的石头个数
则 n - sets 则为需移除的石头个数
// go
var sets int
var father [1000]int
var rowFirst = make(map[int]int) // key: 行号(0..10^4), value: 石头编号(0..1000)
var colFirst = make(map[int]int) // key: 列号(0..10^4), value: 石头编号(0..1000)
func removeStones(stones [][]int) int {
n := len(stones)
build(n)
for i, stone := range stones {
row, col := stone[0], stone[1]
if igot, ok := rowFirst[row]; !ok {
rowFirst[row] = i
} else {
union(i, igot) // 合并 两个石头编号 所在的集合
}
if igot, ok := colFirst[col]; !ok {
colFirst[col] = i
} else {
union(i, igot)
}
}
return n - sets
}
func build(n int) {
clear(rowFirst)
clear(colFirst)
for i := range n {
father[i] = i
}
sets = n
}
func find(i int) int { // 扁平化
if father[i] != i {
father[i] = find(father[i])
}
return father[i]
}
func union(a, b int) {
fa, fb := find(a), find(b)
if fa != fb {
father[fa] = fb
sets--
}
}
二、多语言解法
C p p / G o / P y t h o n / R u s t / J s / T s Cpp/Go/Python/Rust/Js/Ts Cpp/Go/Python/Rust/Js/Ts
// cpp
// go 同上
# python
// rust
// js
// ts