该问题的问题描述如下:
小明正在设计一台打点计数器,该计数器可以接受多个递增的数字范围,并对这些范围内的每个唯一数字打点。如果多个范围之间有重叠,计数器将合并这些范围并只对每个唯一数字打一次点。小明需要你帮助他计算,在给定的多组数字范围内,计数器会打多少个点。 例如,给定三个数字范围 [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
该问题要求在给定的多个数字范围内,计算计数器会打多少个点。打点规则是每个唯一的数字只打一次,即如果多个数字范围有重叠,重叠的部分应该合并计算。我们需要考虑的核心问题是如何合并这些数字范围,并确保每个唯一的数字只计数一次。
思路分析
- 排序范围:首先对所有的数字范围按照起始数字进行排序,这样我们可以依次遍历范围并进行合并。
- 合并重叠范围:在遍历范围时,如果当前范围与前一个范围重叠或紧接着,我们就将它们合并为一个新的范围。如果当前范围不重叠,则可以直接将前一个范围的长度累加到结果中。
- 计算点数:合并后的每个范围的长度就是该范围内所有唯一数字的个数,可以直接计算并累加。
经过简单的思考,我的第一版实现代码如下:
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 是每个范围的长度。这样做虽然能保证正确性,但对于大范围的数字,性能上会受到影响。
于是我们可以进行如下优化:
- 合并范围:目前的代码没有进行范围合并。在多个范围重叠的情况下,每次都在检查每个数字是否存在于集合中,导致重复计算。通过在遍历之前合并重叠范围,可以避免这种重复遍历和判断。
- 避免不必要的集合查找:在合并之后的范围内,我们不再需要检查数字是否已经打点,而是直接计算合并后范围的长度。这样可以显著减少不必要的集合查找操作。
优化后的代码如下:
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