tl; dr:如果您在每次迭代中向前看并向后看,您可以从结尾(最高)开始并O(K)
及时回溯。
尽管我相信这种方法背后的洞察力是合理的,但下面的代码目前还不是很正确(见评论)。
让我们看看:首先,对数组进行排序。因此,如果数组是a
和b
长度为M
和N
,并且按照您对它们的排列,最大的项目分别位于插槽M
和N
中,最大的对总是a[M]+b[N]
。
现在,第二大对是什么?它可能会有一个{a[M],b[N]}
(它不能同时有两个,因为这又是最大的一对),并且至少有一个{a[M-1],b[N-1]}
. 但是,我们也知道,如果我们选择a[M-1]+b[N-1]
,我们可以通过从同一个列表中选择较大的数字来使其中一个操作数更大,因此它的最后一列中只有一个数字,倒数第二列中只有一个数字。
考虑以下两个数组:a = [1, 2, 53]; b = [66, 67, 68]
. 我们的最高对是53+68
。如果我们输掉这两个中较小的那个,我们的对是68+2
; 如果我们失去更大的,它是53+67
。所以,我们必须向前看,以决定我们的下一对将是什么。最简单的前瞻策略是简单地计算两个可能对的总和。这总是需要两次加法,每次转换需要两次比较(三个因为我们需要处理总和相等的情况);让我们称之为成本Q
)。
起初,我很想重复 K-1 次。但是有一个障碍:下一个最大的一对实际上可能是我们可以有效地制作的另一对{{a[M],b[N]}, {a[M-1],b[N-1]}
。所以,我们也需要往后看。
所以,让我们编写代码(python,应该是 2/3 兼容的):
def kth(a,b,k):
M = len(a)
N = len(b)
if k > M*N:
raise ValueError("There are only %s possible pairs; you asked for the %sth largest, which is impossible" % M*N,k)
(ia,ib) = M-1,N-1 #0 based arrays
# we need this for lookback
nottakenindices = (0,0) # could be any value
nottakensum = float('-inf')
for i in range(k-1):
optionone = a[ia]+b[ib-1]
optiontwo = a[ia-1]+b[ib]
biggest = max((optionone,optiontwo))
#first deal with look behind
if nottakensum > biggest:
if optionone == biggest:
newnottakenindices = (ia,ib-1)
else: newnottakenindices = (ia-1,ib)
ia,ib = nottakenindices
nottakensum = biggest
nottakenindices = newnottakenindices
#deal with case where indices hit 0
elif ia <= 0 and ib <= 0:
ia = ib = 0
elif ia <= 0:
ib-=1
ia = 0
nottakensum = float('-inf')
elif ib <= 0:
ia-=1
ib = 0
nottakensum = float('-inf')
#lookahead cases
elif optionone > optiontwo:
#then choose the first option as our next pair
nottakensum,nottakenindices = optiontwo,(ia-1,ib)
ib-=1
elif optionone < optiontwo: # choose the second
nottakensum,nottakenindices = optionone,(ia,ib-1)
ia-=1
#next two cases apply if options are equal
elif a[ia] > b[ib]:# drop the smallest
nottakensum,nottakenindices = optiontwo,(ia-1,ib)
ib-=1
else: # might be equal or not - we can choose arbitrarily if equal
nottakensum,nottakenindices = optionone,(ia,ib-1)
ia-=1
#+2 - one for zero-based, one for skipping the 1st largest
data = (i+2,a[ia],b[ib],a[ia]+b[ib],ia,ib)
narrative = "%sth largest pair is %s+%s=%s, with indices (%s,%s)" % data
print (narrative) #this will work in both versions of python
if ia <= 0 and ib <= 0:
raise ValueError("Both arrays exhausted before Kth (%sth) pair reached"%data[0])
return data, narrative
对于那些没有 python 的人,这里有一个 ideone:http: //ideone.com/tfm2MA
在最坏的情况下,我们在每次迭代中进行 5 次比较,以及 K-1 次迭代,这意味着这是一个 O(K) 算法。
现在,可能可以利用有关值之间差异的信息来稍微优化这一点,但这实现了目标。
这是一个参考实现(不是O(K)
,但将始终有效,除非存在极端情况,即对具有相等的总和):
import itertools
def refkth(a,b,k):
(rightia,righta),(rightib,rightb) = sorted(itertools.product(enumerate(a),enumerate(b)), key=lamba((ia,ea),(ib,eb):ea+eb)[k-1]
data = k,righta,rightb,righta+rightb,rightia,rightib
narrative = "%sth largest pair is %s+%s=%s, with indices (%s,%s)" % data
print (narrative) #this will work in both versions of python
return data, narrative
这将计算两个数组(即所有可能的对)的笛卡尔积,按总和对它们进行排序,并取第 k 个元素。该enumerate
函数用其索引装饰每个项目。