947. 移除最多的同行或同列石头
n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。
如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。
给你一个长度为 n 的数组 stones ,其中 stones[i] = [xi, yi] 表示第 i 块石头的位置,返回 可以移除的石子 的最大数量。
示例 1:
输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
输出:5
解释:一种移除 5 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,1] 同行。
2. 移除石头 [2,1] ,因为它和 [0,1] 同列。
3. 移除石头 [1,2] ,因为它和 [1,0] 同行。
4. 移除石头 [1,0] ,因为它和 [0,0] 同列。
5. 移除石头 [0,1] ,因为它和 [0,0] 同行。
石头 [0,0] 不能移除,因为它没有与另一块石头同行/列。
示例 2:
输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
输出:3
解释:一种移除 3 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,0] 同行。
2. 移除石头 [2,0] ,因为它和 [0,0] 同列。
3. 移除石头 [0,2] ,因为它和 [0,0] 同行。
石头 [0,0] 和 [1,1] 不能移除,因为它们没有与另一块石头同行/列。
解题:并查集
(对连通分量的理解是并查集的核心之一,事实上,由于刚刚接触并查集算法,我会非常自然的忽略掉连通分量在题目中的作用,导致手足无措的局面)
核心思路:可移除石头个数 = 总个数 - 连通分量的个数
目的:求连通分量
注意事项:
(并查集的思想已经很明确,在这里我们只强调几点本题的不同之处)
1.为了区分横纵坐标,我们采用对所有纵坐标做一个变换
可以加上一个参数,例如表长n或者最大表长1000;也可以取负
2.我们要计算连通分量,注意建图时不要出现多余元素
或许是之前遇到的题目都是对一个完整的可迭代对象建图,所以我留下了暴力建图(套用模版)的坏习惯。
在这里,因为石头不是塞满的,而是一个一个给出的,所以我们需要逐步建图的手段。具体体现为类的内置函数add()
又因为暴力建图的.cnt不能使用,所以我们也需要重新定制计算连通分量的手段num_connect_compand()
完整代码
class UnionFind(object):
def __init__(self):
self._father = {
}
def find(self, x):
root = x
while self._father[root] != root:
root = self._father[root]
#路径压缩
while root != x:
origin_root = self._father[x]
self._father[x] = root
x = origin_root
return root
def add(self, x):
if x not in self._father:
self._father[x] = x
def merge(self, x, y):
root_x, root_y = self.find(x), self.find(y)
if root_x != root_y:
self._father[root_y] = root_x
def is_connected(self, x, y):
return self.find(x) == self.find(y)
def num_connect_compand(self):
return sum(1 for x, fa in self._father.items() if x == fa)
class Solution(object):
def removeStones(self, stones):
n = len(stones)
uf = UnionFind()
cnt = 0
for x, y in stones:
uf.add(x)
uf.add(y+10000)
uf.merge(x, y+10000)
return n - uf.num_connect_compand()