刷题实践:小A的移动点 | 豆包MarsCode AI刷题

67 阅读6分钟

问题描述

小M有 n 个点,每个点的坐标为 (xi,yi)。她可以从一个点出发,平行于坐标轴移动,直到到达另一个点。具体来说,她可以从 (x1,y1) 直接到达 (x2,y1) 或者 (x1,y2),但无法直接到达 (x2,y2)。为了使得任意两个点之间可以互相到达,小M可以选择增加若干个新的点。

任务目标:计算最少需要增加多少个点,才能保证任意两个点之间可以通过平行于坐标轴的路径互相到达。

问题剖析

  • 两点之间可以直接到达的条件是,它们要么有相同的 x 坐标,要么有相同的 y 坐标。
  • 要保证所有点都可以通过平行于坐标轴的路径互相到达,意味着我们要将所有点连接成一个连通图。
  • 如果已有点之间存在连通性(即它们共享相同的 x 或 y 坐标),那么这些点形成了一个连通分量。
  • 问题的关键是计算现有的连通分量数量,并确定最少需要多少个新点才能将这些分量连接成一个整体。

解题思路

  1. 图模型:将每个点视作一个节点。如果两个点共享相同的 x 或 y 坐标,它们之间就有一条边。问题变成了计算这些点的连通分量数量。
  2. 并查集(Union-Find) :我们可以使用并查集(Union-Find)来帮助我们合并不同的连通分量。并查集可以高效地管理点之间的连接关系。
  3. 如何合并点:通过 x 坐标和 y 坐标分别将相同 x 或相同 y 的点合并。这样,所有同一坐标轴上的点就可以通过并查集实现连通。
  4. 最小新增点数:计算最终的连通分量数。如果已有的点分成了 k 个连通分量,那么为了使所有点相互连通,我们需要增加 k−1个新点。

解题代码

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [1] * 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:
            # Union by rank
            if self.rank[rootX] > self.rank[rootY]:
                self.parent[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.parent[rootX] = rootY
            else:
                self.parent[rootY] = rootX
                self.rank[rootX] += 1

def solution(n: int, points: list) -> int:
    uf = UnionFind(n)

    # 用字典分别记录相同 x 和相同 y 坐标的点
    x_map = {}
    y_map = {}

    for i in range(n):
        x, y = points[i]
        if x not in x_map:
            x_map[x] = []
        if y not in y_map:
            y_map[y] = []
        x_map[x].append(i)
        y_map[y].append(i)

    # 根据相同的 x 坐标合并
    for indices in x_map.values():
        for i in range(1, len(indices)):
            uf.union(indices[0], indices[i])

    # 根据相同的 y 坐标合并
    for indices in y_map.values():
        for i in range(1, len(indices)):
            uf.union(indices[0], indices[i])

    # 计算连通分量的数量
    root_set = set(uf.find(i) for i in range(n))

    # 最少需要增加的点数 = 连通分量数量 - 1
    return len(root_set) - 1

代码剖析

  1. UnionFind 类
    这是并查集(Union-Find)的实现,支持两个主要操作:

    • find(x):查找元素 x 所在的集合(路径压缩优化)。
    • union(x, y):将元素 xy 合并到同一个集合(按秩合并优化)。
  2. x_map 和 y_map 字典
    使用这两个字典分别记录共享相同 xxx 坐标和相同 yyy 坐标的点的索引。这样我们可以方便地进行合并操作。

  3. 合并操作

    • 遍历 x_mapy_map,对于每一组相同坐标的点,使用 union 方法将它们合并为一个连通分量。
  4. 计算连通分量
    遍历所有点,使用 find 方法找到每个点的根节点,将所有不同的根节点加入集合中,集合的大小即为连通分量的数量。

  5. 返回结果
    最后,返回 连通分量数量 - 1,这是为了将这些连通分量合并成一个整体所需要增加的最小点数。

复杂度分析

  • 时间复杂度

    • 对于每个点,我们需要进行两次合并操作(一次基于 x,一次基于 y)。
    • 并查集的 findunion 操作的时间复杂度是 O(α(n)),其中 α 是反阿克曼函数,几乎是常数。
    • 因此,总的时间复杂度为 O(n),适合处理大规模输入。
  • 空间复杂度

    • 并查集需要 O(n) 的空间来存储父节点和秩信息。
    • 使用两个字典存储坐标映射,空间复杂度也是 O(n)。
    • 总体空间复杂度为 O(n)。

思考总结

  1. 并查集的应用
    本题的核心是通过并查集管理不同点之间的连通性。并查集能够高效地处理合并和查找操作,非常适合解决这种图的连通性问题。
  2. 问题转化的巧妙
    通过将相同 x 和 y 坐标的点视为同一个集合,我们将这个问题转化为计算图的连通分量问题,进而求得需要增加的点数。
  3. 优化技巧
    使用路径压缩和按秩合并来优化并查集的操作,保证了操作的高效性,即使在较大的输入规模下也能迅速完成计算。
  4. 实际应用
    本题的解法可以扩展到其他图连通性相关的问题,特别是那些涉及到坐标或者空间中相对位置关系的问题。

AI是如何帮助我刷题的

AI在我刷题过程中提供了以下帮助:

  1. 问题理解与剖析
    AI能够快速帮助我理解题目中的关键点,并在深入分析后,指出问题的核心所在。例如,在本题中,AI帮助我明确了“平行于坐标轴的路径可达”的本质,就是判断点之间是否存在共享的 x 或 y 坐标。

  2. 解题思路构建
    在面对复杂的问题时,AI可以帮助我从多个角度审视问题,并提供合适的解题策略。通过引导我使用并查集来解决连通性问题,AI帮助我理清了如何通过数据结构有效解决此类图的连通性问题。

  3. 代码实现与优化
    AI不仅帮助我编写出正确的代码,还通过代码优化(如并查集的路径压缩和按秩合并优化),使得解法更加高效。AI的建议让我能够用最简洁高效的方式实现题目要求,避免了冗余和低效的实现。

  4. 复杂度分析与验证
    AI在分析完代码之后帮助我进行时间和空间复杂度的分析,确保解法的高效性并且帮助我理解了其在大规模数据下的表现。

  5. 错误调试与改进
    AI提供了及时的错误排查和改进建议。对于边界情况和特殊输入,AI通过测试案例帮助我确保了代码的正确性和鲁棒性。

    AI不仅仅是代码生成工具,更是一个有力的学习伙伴。它帮助我快速理解题目,提供高效的解法,并且在实际编程中提供了多方面的支持。通过与AI的互动,我能够更快速地掌握和优化算法,提高编程能力。