计算工作对的问题可以在排序时间 + O(N) 内完成。这比 Ani 给出的解决方案更快,即排序时间 + O(N log N)。这个想法是这样的。首先你排序。然后,您运行几乎相同的单遍算法两次。然后,您可以使用这两个单遍算法的结果来计算答案。
第一次运行单遍算法时,我们将创建一个新数组,其中列出了可以与该索引合作的最小索引以给出大于 a 的总和。示例:
a = 6
array = [-20, 1, 3, 4, 8, 11]
output = [6, 4, 2, 2, 1, 1]
因此,数组索引 1 处的数字为 1(基于 0 的索引)。它可以配对以超过 6 的最小数字是 8,它在索引 4 处。因此 output[1] = 4。-20 不能与任何东西配对,所以 output[0] = 6(超出范围) .另一个例子:output[4] = 1,因为 8(索引 4)可以与 1(索引 1)或它后面的任何数字配对,总和大于 6。
你现在需要做的是说服自己这是 O(N)。它是。代码是:
i, j = 0, 5
while i - j <= 0:
if array[i] + array[j] >= a:
output[j] = i
j -= 1
else:
output[i] = j + 1
i += 1
想象一下从边缘开始向内工作的两个指针。是 O(N)。你现在做同样的事情,只是条件 b
while i-j <= 0:
if array[i] + array[j] <= b:
output2[i] = j
i += 1
else:
output2[j] = i-1
j-=1
在我们的示例中,此代码为您提供(数组和 b 供参考):
b = 9
array = [-20, 1, 3, 4, 8, 11]
output2 = [5, 4, 3, 3, 1, 0]
但是现在, output 和 output2 包含了我们需要的所有信息,因为它们包含了配对的有效索引范围。 output 是它可以配对的最小索引, output2 是它可以配对的最大索引。差 + 1 是该位置的配对数。所以对于第一个位置(对应于 -20),有 5 - 6 + 1 = 0 对。对于 1,有 4-4 + 1 对,索引 4 处的数字是 8。另一个微妙之处,这个算法计算自我配对,所以如果你不想要它,你必须减去。例如。 3 似乎包含 3-2 + 1 = 2 对,一个在索引 2,一个在索引 3。当然,3 本身在索引 2,所以其中一个是自配对,另一个是与 4 的配对。只要 output 和 output2 的索引范围包含您正在查看的索引本身,您只需减去一个。在代码中,你可以这样写:
answer = [o2 - o + 1 - (o <= i <= o2) for i, (o, o2) in enumerate(zip(output, output2))]
产量:
answer = [0, 1, 1, 1, 1, 0]
总和为4,对应于(1,8), (3,4), (4,3), (8, 1)
无论如何,如您所见,这是 sort + O(N),这是最优的。
编辑:要求全面实施。假如。供参考,完整代码:
def count_ranged_pairs(x, a, b):
x.sort()
output = [0] * len(x)
output2 = [0] * len(x)
i, j = 0, len(x)-1
while i - j <= 0:
if x[i] + x[j] >= a:
output[j] = i
j -= 1
else:
output[i] = j + 1
i += 1
i, j = 0, len(x) - 1
while i-j <= 0:
if x[i] + x[j] <= b:
output2[i] = j
i += 1
else:
output2[j] = i-1
j -=1
answer = [o2 - o + 1 - (o <= i <= o2) for i, (o, o2) in enumerate(zip(output, output2))]
return sum(answer)/2