英文:
Given two arrays `a` and `b`, find all pairs `(i, j)`such that `i <= j` and `a[i] - b[j] = a[j] - b[i]`
问题
给定两个数组 a
和 b
,找到所有满足条件 i <= j
且 a[i] - b[j] = a[j] - b[i]
的配对 (i, j)
。a
和 b
的长度始终相同。
显然的解决方案是双重循环:
def simple(a, b):
count = 0
for i in range(len(a)):
for j in range(len(b)):
if (a[i] - b[j] == a[j] - b[i] and i <= j):
count += 1
return count
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', simple(a, b)) # 这会打印出 6,配对为 [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]
但这个解决方案的时间复杂度是 O(n^2)。
下面是我提供的 O(n) 解决方案:
我注意到 a[i] - b[j] = a[j] - b[i]
可以写成 a[i] + b[i] = a[j] + b[j]
。
所以我决定创建两个列表 list_i
和 list_j
,它们的长度与 a
和 b
相同。
在 list_i
中,我存储了所有 a[i] + b[i]
的结果。
在 list_j
中,我存储了所有 a[j] + b[j]
的结果。
然后,我只需要比较上面提到的两个列表中每个索引的值,确保它们相同并且 i <= j
。以下是代码,但它是不正确的。
def complex(a, b):
n = len(a)
count = 0
list_i = [0] * n
list_j = [0] * n
for i in range(n):
list_i[i] = a[i] + b[i]
for j in range(n):
list_j[j] = a[j] + b[j]
i, j = 0, 0
while (i <= j and i < n and j < n):
if (list_i[i] == list_j[j]):
count += 1
j += 1
else:
i = j
return count
在这个过程中,我注意到 i
和 j
相同的索引将始终为真。
所以我不需要检查它们。我只需要将 a
的长度添加到最终的计数中。
我还注意到,我只需要检查相邻的值,例如 i = i + 1
。这意味着我只需要一个总和列表,并在循环中检查 sum_list[i] == sum_list[i + 1]
。
因此,我的最终代码如下:
def complex(a, b):
n = len(a)
count = 0
list_sum = [0] * n
for i in range(n):
list_sum[i] = a[i] + b[i]
for i in range(n - 1):
if list_sum[i] == list_sum[i + 1]:
count += 1
return count + len(a)
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', complex(a, b)) # 这会打印出 6,配对为 [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]
这提供了正确的解决方案,并且时间复杂度为 O(n),但是否正确?
另外,是否可以使用集合或字典来找到更好的解决方案?
英文:
NOTE: Yes, this is for a coding challenge, which I already finished, but I want to see if my answer was correct, and if there are more elegant solutions
I got this question on a coding assignment, and I failed to find the optimal O(n) solution
Given two arrays a
and b
, find all pairs (i, j)
such that i <= j
and a[i] - b[j] = a[j] - b[i]
. a
and b
will be the same length at all times.
Give an O(n) solution
The obvious solution is the double for loop:
def simple(a, b):
count = 0
for i in range(len(a)):
for j in range(len(b)):
if (a[i] - b[j] == a[j] - b[i] and i <= j):
count += 1
return count
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', simple(a, b)) # This prints 6, the pairs being [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]
But this solution is O(n^2)
Here is my O(n) solution:
I noticed that a[i] - b[j] = a[j] - b[i]
can be written as a[i] + b[i] = a[j] + b[j]
So i decided to create two lists list_i
and list_j
, both the same length as a
and b
.
In list_i
I stored all the results of a[i] + b[i]
In list_j
I stored all the results of a[j] + b[j]
Then all I had to do was compare the values at each index for the 2 lists mentioned above and make sure they were the same and that i <= j
. The code is below, but it's incorrect.
def complex(a, b):
n = len(a)
count = 0
list_i = [0] * n
list_j = [0] * n
for i in range(n):
list_i[i] = a[i] + b[i]
for j in range(n):
list_j[j] = a[j] + b[j]
i, j = 0, 0
while (i <= j and i < n and j < n):
if (list_i[i] == list_j[j]):
count += 1
j += 1
else:
i = j
return count
While doing that, I noticed that the index where i
and j
are the same, will always be true.
So I don't have to check them. I can simply add the line of a
to the final count.
I also noticed that I only needed to check adjacent values such that i = i + 1
.
This meant that I only had to have one list of sums and in a for loop check to see if sum_list[i] == sum_list[i + 1]
So my final code is as follows:
def complex(a, b):
n = len(a)
count = 0
list_sum = [0] * n
for i in range(n):
list_sum[i] = a[i] + b[i]
for i in range(n - 1):
if list_sum[i] == list_sum[i + 1]:
count += 1
return count + len(a)
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', complex(a, b)) # This prints 6, the pairs being [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]
This gives the correct solution and is O(n), but is it correct?
Also is there a better solution using sets or dicts?
答案1
得分: 4
如果您只对成对数量感兴趣,而不关心具体的成对元素,可以使用字典在O(n)时间内解决这个问题。
首先,让c
成为a
和b
的逐元素求和(即c = [ai + bi for ai, bi in zip(a, b)]
)。注意到约束条件a[i] + b[i] == a[j] + b[j]
意味着我们要找到i, j
使得c[i] == c[j]
。
对于c
的每个唯一值v
,如果有n
个索引使得c[i] == v
,那么就会有n * (n + 1) // 2
对索引使得c[i] == c[j]
且i <= j
。因此,我们只需计算每个唯一值的c
的索引数量,并应用以下公式:
from collections import Counter
def count_pairs(a, b):
c = [ai + bi for ai, bi in zip(a, b)]
ctr = Counter(c)
return sum(n * (n + 1) // 2 for n in ctr.values())
这在摊销时间O(n)内运行:构建c
需要O(n)时间,构建ctr
需要摊销时间O(n)(它是一个仅存储每个唯一值计数的dict
),计算总和需要O(n)时间(计算仅在c
的每个唯一值中运行一次,最多有n
个这样的唯一值)。
英文:
If you're only interested in the number of pairs, not the pairs themselves, this can be solved in O(n) with a dictionary.
First, let c
be the elementwise sum of a
and b
(i.e. c = [ai + bi for ai, bi in zip(a, b)]
). Observe that the constraint a[i] + b[i] == a[j] + b[j]
implies that we're looking for i, j
such that c[i] == c[j]
.
For each unique value v
of c
, if there are n
indices such that c[i] == v
, then there will be n * (n + 1) // 2
pairs of indices such that c[i] == c[j]
and i <= j
. Therefore, all we need to do is count the number of indices per unique value of c
and apply this formula:
from collections import Counter
def count_pairs(a, b):
c = [ai + bi for ai, bi in zip(a, b)]
ctr = Counter(c)
return sum(n * (n + 1) // 2 for n in ctr.values())
This runs in O(n) amortized time: constructing c
takes O(n) time, constructing ctr
takes O(n) amortized time (it is a dict
which stores only the count of each unique value), and computing the sum takes O(n) time (the calculation runs once per unique value in c
, and there are at most n
such unique values).
答案2
得分: 2
一个简单的O(N)
解决方案可以通过使用哈希映射来实现,这允许O(1)的查找。
英文:
An easy O(N)
solution can be found by using a hashmap, which allows O(1) lookup.
答案3
得分: 0
我还注意到只需要检查相邻的值。
如果list_sum
列表已排序,则是正确的,但在一般情况下不是如此(例如,如果list_sum
是[10, 20, 10]
,来自于a = [1, 2, 3]
,b = [9, 18, 7]
)。
因此,要修复您的代码,在第3个for
循环之前执行list_sum.sort()
。不过这会使其变慢,因为排序需要O(N * log(N))
的时间。
此外,您可能需要更多地计算相同值的较长运行。如果list_sum
是[10, 10, 10]
,您的complex函数返回5,但它应该返回6才对。
英文:
> I also noticed that I only needed to check adjacent values
This is true if the list_sum
list is sorted, but it's not true in the general case (e.g. if list_sum
is [10, 20, 10]
, coming from e.g. a = [1, 2, 3]
, b = [9, 18, 7]
).
So to fix your code, do a list_sum.sort()
before your 3rd for
loop. This will make it slower though, because the sorting takes O(N * log(N))
time.
Also you may have to count longer runs of the same value more. Your complex function returns 5 if list_sum
is [10, 10, 10]
, but shouldn't it return 6 instead?
答案4
得分: 0
"给出一个O(n)的解决方案"
嗯...考虑一下当a
和b
都是包含n
个0的数组时的情况,例如:
n = 2
a = [0, 0]
b = [0, 0]
解决方案 = [[0, 0], [0, 1], [1, 1]]
解决方案长度: 3
n = 3
a = [0, 0, 0]
b = [0, 0, 0]
解决方案 = [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
解决方案长度: 6
n = 4
a = [0, 0, 0, 0]
b = [0, 0, 0, 0]
解决方案 = [[0, 0], [0, 1], [0, 2], [0, 3], [1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
解决方案长度: 10
一般情况下,当a
和b
是包含n
个0的数组时,解决方案的长度是三角数;f(n) = n * (n + 1) * 1/2
- 这是二次增长,看起来不可能在O(n^2)
时间内找到更快的方法。
我们是否遗漏了一些问题的细节?
- 例如,返回配对数而不是配对本身?
a
和b
是否已排序?a
和b
中的项目是否保证是唯一的?
英文:
"Give an O(n) solution"
Hmmmm... consider cases where a
and b
are both arrays containing n
0s each, e.g.:
n = 2
a = [ 0, 0 ]
b = [ 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 1, 1 ] ]
solution length: 3
n = 3
a = [ 0, 0, 0 ]
b = [ 0, 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 1, 1 ], [ 1, 2 ], [ 2, 2 ] ]
solution length: 6
n = 4
a = [ 0, 0, 0, 0 ]
b = [ 0, 0, 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 0, 3 ], [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 2 ], [ 2, 3 ], [ 3, 3 ] ]
solution length: 10
In general when a
and b
are arrays of n
0s, the length of the solution is the triangular numbers; f(n) = n * (n + 1) * 1/2
- this is quadratic growth, and doesn't look to me like it will ever be captured in less than O(n^2)
time.
Are we missing some details from the question?
- e.g. return the number of pairs, not the pairs themselves?
- are
a
andb
sorted? - are items in
a
andb
guaranteed to be unique?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论