刷题笔记-小A的移动点 | 豆包MarsCode AI刷题

129 阅读5分钟

www.marscode.cn/practice/vk…

题目要求我们通过增加一些点,使得给定的所有点能够通过平行于坐标轴的路径互相到达。简单来说,我们可以在 xx-轴或 yy-轴方向上移动,因此问题的核心是如何通过平行于坐标轴的路径将所有点连接起来。

关键点

  1. 坐标平行性:题目明确指出,我们可以通过平行于坐标轴的路径连接点。这意味着:

    • 如果两个点的 xx 坐标相同,则它们可以通过一条平行于 yy-轴的路径相连;
    • 如果两个点的 yy 坐标相同,则它们可以通过一条平行于 xx-轴的路径相连。
  2. 目标:我们需要确保所有点之间都能互相到达。为了实现这一点,我们可以通过“合并”具有相同 xxyy 坐标的点来减少需要增加的点数。我们最终需要让所有点都处于同一个“联通分量”中。

  3. 联通分量:这是一个图论问题,每个点可以视为一个图的节点,两个点之间有边(可以连通)当且仅当它们的 xxyy 坐标相同。为了保证所有点都连通,我们需要通过并查集(Union-Find)来管理这些点的联通性。

解题思路

我们将问题转化为“联通分量”的问题,具体来说,我们需要:

  1. 构建并查集:使用并查集(Union-Find)来管理各个点的联通性。当两个点具有相同的 xxyy 坐标时,我们可以将它们合并到同一个联通分量中。

  2. 映射坐标:通过两个字典分别记录每个 xx 和每个 yy 坐标,遇到相同坐标的点时,直接将它们所在的点连接起来。

  3. 计算联通分量数:最后,我们通过并查集找出有多少个独立的联通分量。我们需要增加的点数就是联通分量数减去 1,因为为了连接所有的联通分量,我们最少需要连接 k1k-1 条边,其中 kk 是联通分量的个数。

算法设计

  1. 初始化

    • 使用并查集数据结构来管理点的联通性。每个点一开始是独立的,属于自己的联通分量。
  2. 构建坐标映射

    • 我们为每个点记录它的 xxyy 坐标。对于每个点,如果它的 xxyy 坐标与另一个点相同,则将这两个点合并为一个联通分量。
  3. 联通分量统计

    • 最后,遍历所有的点,统计它们的根节点,根节点的数量即为联通分量的数量。
    • 需要增加的点数为联通分量数减去 1,因为我们通过增加的点来连接不同的联通分量。

代码实现

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
    
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            self.parent[rootX] = rootY

def solution(n: int, points: list) -> int:
    if n <= 1:
        return 0

    # Union-Find 初始化
    uf = UnionFind(n)
    
    # 使用字典记录每个x和y值出现的点的索引
    x_map = {}
    y_map = {}
    
    for i in range(n):
        x, y = points[i]
        
        # 如果相同x值的点还没有出现,进行映射
        if x not in x_map:
            x_map[x] = i
        else:
            uf.union(i, x_map[x])
        
        # 如果相同y值的点还没有出现,进行映射
        if y not in y_map:
            y_map[y] = i
        else:
            uf.union(i, y_map[y])

    # 计算联通分量的数量
    root_set = set()
    for i in range(n):
        root_set.add(uf.find(i))
    
    # 需要增加的点数是联通分量数减去 1
    return len(root_set) - 1

算法分析

1. 并查集(Union-Find)

并查集(Union-Find)是一种高效的数据结构,用于处理不相交集合的合并及查询操作。在我们的算法中,使用并查集来动态合并具有相同坐标的点。具体操作如下:

  • find(x):查找元素 xx 的根节点,使用路径压缩技术来提高效率。
  • union(x, y):合并元素 xxyy 所在的集合,使用按秩合并(union by rank)来优化合并过程。

这两种操作的时间复杂度接近常数时间,即 O(α(n))O(\alpha(n)),其中 α(n)\alpha(n) 是阿克曼函数的反函数,增长极慢,几乎可以视为常数。

2. 空间复杂度

  • 我们使用了两个字典 x_mapy_map 来存储每个坐标值的出现位置,这两个字典的空间复杂度是 O(n)O(n)
  • 并查集的空间复杂度是 O(n)O(n),因为每个点都有一个对应的父节点。

因此,整体空间复杂度是 O(n)O(n)

3. 时间复杂度

  • 处理每个点:对于每个点,我们执行两次 find 和最多一次 union 操作,平均时间复杂度是 O(α(n))O(\alpha(n))
  • 遍历所有点统计联通分量:遍历所有点进行查找的时间复杂度是 O(nα(n))O(n \cdot \alpha(n))

因此,总时间复杂度为 O(nα(n))O(n \cdot \alpha(n)),可以认为是接近线性时间。

复杂度总结

  • 时间复杂度O(nα(n))O(n \cdot \alpha(n)),其中 α(n)\alpha(n) 为阿克曼函数的反函数,几乎是常数。
  • 空间复杂度O(n)O(n)

总结

通过并查集,我们能够高效地管理点之间的联通关系。我们通过将所有具有相同 xxyy 坐标的点合并到同一个联通分量,最终求出联通分量的数量,并计算出最少需要增加的点数。这个解法具有较好的时间和空间效率,能够应对较大规模的数据。