题目描述
小M有n个点,每个点的坐标为。她可以从一个点出发,平行于坐标轴移动,直到到达另一个点。具体来说,她可以从 直接到达 或者 ,但无法直接到达 。为了使得任意两个点之间可以互相到达,小M可以选择增加若干个新的点。
你的任务是计算最少需要增加多少个点,才能保证任意两个点之间可以通过平行于坐标轴的路径互相到达。
问题分析
本题是关于图连通性的一个经典问题,要求计算在一个二维平面上,给定一些点,通过添加最少的点使得任意两点之间都可以通过平行于坐标轴的路径互相到达。这里的路径只能沿着x轴或y轴移动,不能斜着走。
为了解决这个问题,我们可以将其转化为图论问题。将每个点看作图中的一个节点,如果两个点共享相同的x坐标或y坐标,则它们之间存在一条边。我们的目标是找到最少的边(通过添加点来构造),使得整个图变成一个连通图。
并查集介绍
并查集是一种数据结构,用于处理一些不交集的合并及查询问题。它可以高效地管理元素的分组情况,支持两种主要操作:
- 查找(Find):确定某个元素属于哪个集合(或称为“组”或“连通分量”)。
- 合并(Union):将两个集合合并成一个集合。
并查集通常通过树形结构来实现,其中每个节点都指向它的父节点,根节点指向自己。在合并两个集合时,我们找到它们的根节点,并将其中一个根节点的父节点设置为另一个根节点,从而实现合并。为了提高查找效率,通常还会使用路径压缩技术,即在查找过程中将路径上的每个节点直接连接到根节点。
解题思路
-
初始化并查集:首先,我们需要创建一个并查集来管理所有的点,在本题中,使用点的索引作为并查集的索引。
-
合并集合:由于本题的连通关系并不是直接给出的,我们需要遍历所有的点,根据其x坐标和y坐标分组,同集合中的点是连通的。遍历所有的分组,对每个分组的第一个点,我们将其与后续的点进行合并。这样,所有在同一行或同一列上的点都会被合并到同一个集合中。
-
计算连通分量:最后,我们统计并查集中连通分量的数量。如果连通分量的数量为k,那么我们需要添加k-1个点来使整个图连通。因为每添加一个点,最多可以减少一个连通分量(即该点连接了两个原本不连通的分量)。
实现代码
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, u):
if self.parent[u] != u:
self.parent[u] = self.find(self.parent[u])
return self.parent[u]
def union(self, u, v):
root_u = self.find(u)
root_v = self.find(v)
if root_u != root_v:
if self.rank[root_u] > self.rank[root_v]:
self.parent[root_v] = root_u
elif self.rank[root_u] < self.rank[root_v]:
self.parent[root_u] = root_v
else:
self.parent[root_v] = root_u
self.rank[root_u] += 1
def solution(n: int, points: list) -> int:
ds=DisjointSet(n)
x_lists:dict[int,list]={}
y_lists:dict[int,list]={}
for i,p in enumerate(points):
if p[0] not in x_lists:
x_lists[p[0]] = []
x_lists[p[0]].append(i)
if p[1] not in y_lists:
y_lists[p[1]] = []
y_lists[p[1]].append(i)
for k in x_lists:
x_list=x_lists[k]
for i in range(1,len(x_list)):
ds.union(x_list[0],x_list[i])
for k in y_lists:
y_list=y_lists[k]
for i in range(1,len(y_list)):
ds.union(y_list[0],y_list[i])
ans=-1
for i in range(n):
if i==ds.find(i):
ans+=1
return ans