"70. 打点计数器的区间合并"题解 | 豆包MarsCode AI刷题

106 阅读3分钟

该问题的问题描述如下:

小明正在设计一台打点计数器,该计数器可以接受多个递增的数字范围,并对这些范围内的每个唯一数字打点。如果多个范围之间有重叠,计数器将合并这些范围并只对每个唯一数字打一次点。小明需要你帮助他计算,在给定的多组数字范围内,计数器会打多少个点。 例如,给定三个数字范围 [1, 4], [7, 10], 和 [3, 5],计数器首先将这些范围合并,变成 [1, 5] 和 [7, 10],然后计算这两个范围内共有多少个唯一数字,即从 1 到 5 有 5 个数字,从 7 到 10 有 4 个数字,共打 9 个点。

题目所给的示例如下:

输入:inputArray = [[1, 4], [7, 10], [3, 5]] 输出:9
输入:inputArray = [[1, 2], [6, 10], [11, 15]] 输出:12
输入:inputArray = [[1, 3], [2, 6], [8, 10]] 输出:9

该问题要求在给定的多个数字范围内,计算计数器会打多少个点。打点规则是每个唯一的数字只打一次,即如果多个数字范围有重叠,重叠的部分应该合并计算。我们需要考虑的核心问题是如何合并这些数字范围,并确保每个唯一的数字只计数一次。

思路分析

  1. 排序范围:首先对所有的数字范围按照起始数字进行排序,这样我们可以依次遍历范围并进行合并。
  2. 合并重叠范围:在遍历范围时,如果当前范围与前一个范围重叠或紧接着,我们就将它们合并为一个新的范围。如果当前范围不重叠,则可以直接将前一个范围的长度累加到结果中。
  3. 计算点数:合并后的每个范围的长度就是该范围内所有唯一数字的个数,可以直接计算并累加。

经过简单的思考,我的第一版实现代码如下:

def solution(inputArray): 
    num_of_dot = 0 
    is_type = set() 
    i = 0 
    while i < len(inputArray): 
        ii = inputArray[i][0] 
        while ii <= inputArray[i][1]: 
            if ii not in is_type: 
                is_type.add(ii) 
                num_of_dot += 1 
            ii += 1 
        i += 1 
     return num_of_dot

上述代码通过逐个遍历每个数字范围,并将每个唯一数字加入一个集合 is_type,确保每个数字只打一次点。每次遇到一个新的数字时,增加 num_of_dot 计数。

虽然事实证明这种方法是正确的,但其实还在效率上可以做一些优化,即:

代码使用了set 来存储已经打过点的数字以确保不会重复计数,会导致每次遍历到一个新数字时都要检查它是否已经存在于集合中,这个过程对于每个范围的每个数字都会进行,因此时间复杂度是 O(n * m),其中 n 是范围的数量,m 是每个范围的长度。这样做虽然能保证正确性,但对于大范围的数字,性能上会受到影响。

于是我们可以进行如下优化:

  1. 合并范围:目前的代码没有进行范围合并。在多个范围重叠的情况下,每次都在检查每个数字是否存在于集合中,导致重复计算。通过在遍历之前合并重叠范围,可以避免这种重复遍历和判断。
  2. 避免不必要的集合查找:在合并之后的范围内,我们不再需要检查数字是否已经打点,而是直接计算合并后范围的长度。这样可以显著减少不必要的集合查找操作。

优化后的代码如下:

def solution(inputArray):
    # 排序范围
    inputArray.sort()

    # 合并范围并计算点数
    merged = []
    for start, end in inputArray:
        if not merged or merged[-1][1] < start:
            merged.append([start, end])
        else:
            merged[-1][1] = max(merged[-1][1], end)

    # 计算打点数
    num_of_dot = 0
    for start, end in merged:
        num_of_dot += end - start + 1

    return num_of_dot