问题描述
小明正在设计一台打点计数器,该计数器可以接受多个递增的数字范围,并对这些范围内的每个唯一数字打点。如果多个范围之间有重叠,计数器将合并这些范围并只对每个唯一数字打一次点。小明需要你帮助他计算,在给定的多组数字范围内,计数器会打多少个点。
例如,给定三个数字范围 [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. 唯一数字的计算:
- 对于一个区间
[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 )
代码逐行分析
-
输入为空的处理:
if not inputArray: return 0- 这行代码首先检查输入
inputArray是否为空。如果为空,直接返回0,因为没有区间可合并,结果显然是0。
- 这行代码首先检查输入
-
排序区间:
inputArray.sort()- 通过
sort()对所有的区间进行排序,默认是按区间的起始位置l排序。这确保了我们可以按顺序处理每个区间,避免处理顺序混乱。
- 通过
-
初始化变量:
l, r = inputArray[0] ans = 0l, r = inputArray[0]:取第一个区间的起始位置和结束位置,作为初始的区间。ans = 0:用来累加所有合并后的区间中的数字个数。
-
遍历区间并合并:
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),合并区间。
- 仅更新结束位置
-
-
处理最后一个区间:
ans += r - l + 1- 在遍历完成后,别忘了加上最后一个区间的数字个数。
-
返回结果:
return ans- 最终返回合并后的所有区间的总数字个数。
复杂度分析
-
时间复杂度:
- 排序:排序所有区间需要
O(n log n)时间,其中n是区间的数量。 - 合并:遍历所有区间,进行合并操作的时间复杂度为
O(n)。
因此,整体时间复杂度是
O(n log n),排序是主要的时间开销。 - 排序:排序所有区间需要
-
空间复杂度:
- 代码中只使用了常数的额外空间来存储变量
l,r,ans,以及排序过程中占用的临时空间。
因此,空间复杂度为
O(1),如果不考虑排序过程中的空间消耗。 - 代码中只使用了常数的额外空间来存储变量