有了上面的知识,其实就可以将石子全部建立并查集的联系,并计算联通子图的个数。答案就是 n - 联通子图的个数,其中 n 为 stones 的长度。
核心代码:
n = len(stones)
# 标准并查集模板
uf = UF(n)
# 两个 for 循环作用是将所有石子两两合并
for i in range(n):
for j in range(i + 1, n):
# 如果行或者列相同,将其联通成一个子图
if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]: uf.union(i, j)
return n - uf.cnt
为了达到这个模板,我们不能再初始化的时候计算联通域数量了,即不能像上面那样 uf = UF(n)(此时联通域个数为 n)。因为横坐标,纵坐标分别有多少不重复的我们是不知道的,一种思路是先计算出横坐标,纵坐标分别有多少不重复的。这当然可以,还有一种思路是在 find 过程中计算,这样 one pass 即可完成,具体见下方代码区。
由于我们需要区分横纵坐标(上面图也可以看出来),因此可用一种映射区分二者。由于题目限定了横纵坐标取值为 10000 以内(包含 10000),一种思路就是将 x 或者 y 加上 10001。
核心代码:
n = len(stones)
uf = UF(0)
for i in range(n):
uf.union(stones[i][0] + 10001, stones[i][1])
return n - uf.cnt
代码
并查集
代码支持: Python3
class UF:
def __init__(self, M):
self.parent = {}
self.cnt = 0
# 初始化 parent,size 和 cnt
for i in range(M):
self.parent[i] = i
self.cnt += 1
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
if self.connected(p, q): return
leader_p = self.find(p)
leader_q = self.find(q)
self.parent[leader_p] = leader_q
self.cnt -= 1
def connected(self, p, q):
return self.find(p) == self.find(q)
class Solution:
def removeStones(self, stones: List[List[int]]) -> int:
n = len(stones)
uf = UF(n)
for i in range(n):
for j in range(i + 1, n):
if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]: uf.union(i, j)
return n - uf.cnt
复杂度分析
令 n 为数组 stones 的长度。
时间复杂度:$O(n^2logn)$
空间复杂度:$O(n)$
优化的并查集
代码支持: Python3
class UF:
def __init__(self, M):
self.parent = {}
self.cnt = 0
def find(self, x):
if x not in self.parent:
self.cnt += 1
self.parent[x] = x
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
if self.connected(p, q): return
leader_p = self.find(p)
leader_q = self.find(q)
self.parent[leader_p] = leader_q
self.cnt -= 1
def connected(self, p, q):
return self.find(p) == self.find(q)
class Solution:
def removeStones(self, stones: List[List[int]]) -> int:
n = len(stones)
uf = UF(0)
for i in range(n):
uf.union(stones[i][0] + 10001, stones[i][1])
return n - uf.cnt
复杂度分析
令 n 为数组 stones 的长度。
时间复杂度:$O(nlogn)$
空间复杂度:$O(n)$
DFS
代码支持: Java
by 一位不愿透露姓名的热心网友。
public int removeStones(int[][] stones) {
Set visit = new HashSet();
int count = 0;
int offset = 10000;
HashMap >map = new HashMap();
// 构造图 每一项是一个节点
for (int i = 0; i < stones.length; i++) {
int [] node = stones[i];
List list = map.getOrDefault(node[0]-offset,new ArrayList<>());
list.add(node);
map.put(node[0]-offset,list);
List list1 = map.getOrDefault(node[1],new ArrayList<>());
list1.add(node);
map.put(node[1],list1);
}
// 寻找联通分量
for (int i = 0; i < stones.length; i++) {
int [] node = stones[i];
if (!visit.contains((node))){
visit.add((node));
dfs(node,visit,map);
count++;
}
}
return stones.length-count;
}
// 遍历节点
public void dfs(int[]node, Set set,HashMap >map){
int offset = 10000;
List list = map.getOrDefault(node[0]-offset,new ArrayList<>());
for (int i = 0; i < list.size(); i++) {
int[] item = list.get(i);
if (!set.contains((item))){
set.add((item));
dfs(item,set,map);
}
}
List list2 = map.getOrDefault(node[1],new ArrayList<>());
for (int i = 0; i < list2.size(); i++) {
int[] item = list2.get(i);
if (!set.contains((item))){
set.add((item));
dfs(item,set,map);
}
}
}
复杂度分析
令 n 为数组 stones 的长度。
时间复杂度:建图和遍历图的时间均为 $O(n)$
空间复杂度:$O(n)$
力扣的小伙伴的点下我头像的关注按钮,这样就会第一时间收到我的动态啦~
以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 38K star 啦。