我必须找到一种解决以下问题的算法:
输入是两个自然数S和k,以及一组n对成对的不同数的无序集合。
确定O(n)是否有k个数字的子集加起来等于<= S。注意:k不应成为时间复杂度的一部分。
algorithm({x_1, ..., x_n}, k, S):
if exists |{x_i, ..., x_j}| = k and x_i + ... x_j <= S return true
我找不到时间复杂度为O(n)的解决方案。
我能得到的是O(kn),因为我们搜索k倍最小值并求和:
algorithm(a={x_1, ..., x_n}, k, S):
sum = 0
for i=1,...,k:
min = a.popFirst()
for i=2,...,len(a):
if(a[i] < min):
t = a[i]
a[i] = min
min = t
sum += min
if sum <= S:
return true
else:
return false
这在O(n)中,并返回正确的结果。我怎样才能松开k?
感谢您的帮助,我真的很努力!
答案 0 :(得分:3)
快速选择可用于找到 k 个最小元素:https://en.wikipedia.org/wiki/Quickselect
基本上,它是快速排序,只是您只在轴的有趣一侧进行递归。
一个简单的实现可以在 O(N) 预期时间内运行,但是使用中位数中位数来选择枢轴,则可以使情况变得更糟绑定:https://en.wikipedia.org/wiki/Median_of_medians
答案 1 :(得分:2)
您可以根据集合构建大小为k
的最小堆。建立时间的复杂度是O(n)
个预期时间,O(n log k)
个最坏的情况。
堆应包含集合中的前k
个最小元素。
然后可以很容易地看到堆中元素的总和为<= S
。您无需从堆中删除元素即可计算总和。只需遍历堆以计算总和。删除所有元素会带来k log k
的复杂性。
您甚至不需要考虑下一个较高的元素,因为相加会导致总和大于S