题目解析:打点计数器的区间合并 | 豆包MarsCode AI 刷题

54 阅读6分钟

问题描述

小明正在设计一台打点计数器,该计数器可以接受多个递增的数字范围,并对这些范围内的每个唯一数字打点。如果多个范围之间有重叠,计数器将合并这些范围并只对每个唯一数字打一次点。小明需要你帮助他计算,在给定的多组数字范围内,计数器会打多少个点。

例如,给定三个数字范围 [1, 4], [7, 10], 和 [3, 5],计数器首先将这些范围合并,变成 [1, 5] 和 [7, 10],然后计算这两个范围内共有多少个唯一数字,即从 1 到 5 有 5 个数字,从 7 到 10 有 4 个数字,共打 9 个点。


测试样例

样例1:

输入:inputArray = [[1, 4], [7, 10], [3, 5]]
输出:9

样例2:

输入:inputArray = [[1, 2], [6, 10], [11, 15]]
输出:12

样例3:

输入:inputArray = [[1, 3], [2, 6], [8, 10]]
输出:9

解题思路

题目要求我们处理多个递增的数字区间,合并重叠部分,并计算所有合并后的区间中的唯一数字个数。这个问题涉及到区间合并去重计算两个核心概念。

理解问题本质

我们有多个区间,其中每个区间都有一个起始数字和结束数字。目标是:

  1. 对所有区间进行合并,去掉重复的部分。
  2. 计算所有合并后的区间内的数字总数。

核心问题是如何合并这些区间,并正确计算合并后的区间中的数字个数。

关键点分析

1. 区间合并的原则:

  • 如果两个区间没有重叠,那么它们应当被独立处理。
  • 如果两个区间有重叠部分,则应将它们合并成一个区间,去掉重复的数字。

2. 唯一数字的计算:

  • 对于一个区间 [l, r],其包含的数字是从 l 到 r,一共 r - l + 1 个数字。
  • 所以,合并区间后,我们需要计算每个合并区间的长度,然后求和。

解决方法

1. 排序区间

由于每个区间的起始位置和结束位置都有顺序关系(例如,区间 [l1, r1] 总是会比 [l2, r2] 小,当 l1 < l2 时),我们可以首先对所有区间进行排序。这样排序后的区间,可以更方便地进行合并操作。

  • 排序的原因:排序可以保证,如果两个区间没有重叠,顺序一定是连续的。因此,排序后,我们就可以逐个检查相邻的区间,看它们是否重叠。
2. 区间合并过程

合并区间的过程如下:

  • 初始化第一个区间的起始位置和结束位置,l 和 r

  • 对于每个新的区间 [pl, pr]

    • 如果当前区间 [l, r] 和新的区间 [pl, pr] 不重叠(即 r < pl),则:

      • 计算当前区间 [l, r] 内的数字个数,并将其加入总数。
      • 更新当前区间为新的区间 [pl, pr]
    • 如果当前区间 [l, r] 和新的区间 [pl, pr] 重叠(即 r >= pl),则:

      • 更新当前区间的结束位置为两个区间结束位置的较大值 r = max(r, pr)

这个过程会在遍历所有区间之后结束,最后不要忘了再加上最后一个区间的长度。

3. 最终计算

每次合并区间后,我们会根据 r - l + 1 计算当前区间的数字个数并累加到总和中。遍历完所有区间后,返回最终结果。

代码实现

我们在代码中按照这个思路逐步实现了区间的排序、合并和数字个数的计算。

def solution(inputArray):
    if not inputArray:
        return 0

    # 先对区间进行排序
    inputArray.sort()
    
    # 初始化第一个区间的边界
    l, r = inputArray[0]
    ans = 0
    
    for i in range(0, len(inputArray)):
        pl, pr = inputArray[i]
        if r < pl:
            # 当前区间与前一个区间不重叠
            ans += r - l + 1
            l, r = pl, pr
        else:
            # 当前区间与前一个区间重叠
            r = max(r, pr)
    
    # 加上最后一个区间的点数
    ans += r - l + 1
    
    return ans

if __name__ == "__main__":
    #  You can add more test cases here
    testArray1 = [[1,4], [7, 10], [3, 5]]
    testArray2 = [[1,2], [6, 10], [11, 15]]
    testArray3 = [[6,18],[2,16],[12,16],[5,16],[8,10],[1,9],[7,21],[2,3],[7,21],[6,7],[1,24],[9,17],[1,4],[12,18],[2,17],[4,19],[9,22],[8,24],[13,21],[7,8],[19,22],[22,23],[6,14]]

    print(solution(testArray1) == 7 )
    print(solution(testArray2) == 9 )
    print(solution(testArray3) == 23 )

代码逐行分析

  1. 输入为空的处理:

    if not inputArray:
        return 0
    
    • 这行代码首先检查输入 inputArray 是否为空。如果为空,直接返回 0,因为没有区间可合并,结果显然是 0
  2. 排序区间:

    inputArray.sort()
    
    • 通过 sort() 对所有的区间进行排序,默认是按区间的起始位置 l 排序。这确保了我们可以按顺序处理每个区间,避免处理顺序混乱。
  3. 初始化变量:

    l, r = inputArray[0]
    ans = 0
    
    • l, r = inputArray[0]:取第一个区间的起始位置和结束位置,作为初始的区间。
    • ans = 0:用来累加所有合并后的区间中的数字个数。
  4. 遍历区间并合并:

    for i in range(1, len(inputArray)):
        pl, pr = inputArray[i]
        if r < pl:
            ans += r - l + 1
            l, r = pl, pr
        else:
            r = max(r, pr)
    
    • 遍历从第二个区间开始的所有区间。

    • 如果当前区间 [pl, pr] 和前一个区间 [l, r] 不重叠(即 r < pl),则:

      • 将前一个区间的数字个数 r - l + 1 加到 ans
      • 更新当前区间为 [pl, pr]
    • 如果当前区间 [pl, pr] 和前一个区间 [l, r] 重叠(即 r >= pl),则:

      • 仅更新结束位置 r 为 max(r, pr),合并区间。
  5. 处理最后一个区间:

    ans += r - l + 1
    
    • 在遍历完成后,别忘了加上最后一个区间的数字个数。
  6. 返回结果:

    return ans
    
    • 最终返回合并后的所有区间的总数字个数。

复杂度分析

  1. 时间复杂度:

    • 排序:排序所有区间需要 O(n log n) 时间,其中 n 是区间的数量。
    • 合并:遍历所有区间,进行合并操作的时间复杂度为 O(n)

    因此,整体时间复杂度是 O(n log n),排序是主要的时间开销。

  2. 空间复杂度:

    • 代码中只使用了常数的额外空间来存储变量 lrans,以及排序过程中占用的临时空间。

    因此,空间复杂度为 O(1),如果不考虑排序过程中的空间消耗。