有没有办法在O(n)
时间内获得k
排序数组中元素的组合,这样可以得出组合中最小和最大元素之间的最小差异? n
是这些数组中元素的总数。
以下是一个例子:
array1 = [11]
array2 = [5,7]
array3 = [6,18]
对于那些阵列,以下是所有可能的组合:
comb1 = [11, 5, 6]
comb2 = [11, 7, 6]
comb3 = [11, 5, 18]
comb4 = [11, 7, 18]
在这种情况下,对于上述组合,最小和最大元素的差异分别为6, 5, 13, 11
。所以,我应该返回的是comb2
,因为该组合的差异是最小的。
如果它有帮助,原始数组中的整个值集中没有重复元素。
答案 0 :(得分:2)
将数字分别排列在一起,以及每个数组:
a: 11
b: 5, 7
c: 6, 18
5, 6, 7, 11, 18
b1 c1 b2 a1 c2
对于任何选择,我们使不删除外部数字(右侧或最左侧),下一个选择顺序是明确的。如果我们选择修复c2
(18),我们可以从左侧前往b2
。但是如果我们删除c2
,则左边界中唯一的变化是在c
数组本身内。
从最左边开始,我们可以从固定最右边的元素开始。在任何时候,如果元素是任何一个数组中最后两个元素之一,则从右侧中删除元素只会移动左边界。继续删除最右边的元素,每次更新左边界(如果需要)。选择最佳的间隔时间。 (注意外部两个元素之间的元素无关紧要,我们可以从每个数组中选择任何一个作为代表。)
排序后滑动窗口迭代的复杂度为O(n)
,因为对于排序数组,累积左边绑定更新为O(n)
。
答案 1 :(得分:1)
TL; DR 我认为这不可能在O(n)
中进行(虽然我无法证明),所以下面你可以找到两个O(n*logk)
解决方案
解决方案1:
使用here所述的大小为k
的最小堆。
解决方案2 (我的)
我们还需要3个额外的阵列,我们称之为A
,B
和C
。我们也将输入数组称为“原始数组”。
A
的大小为n
,并会按排序顺序保留所有原始数组中的所有元素B
也会有n
大小,并会保留有关数组A
中元素来源的信息,即A
中的原始数组元素来自C
的大小为k
,它将包含在此过程中原始数组中最后看到的元素索引(见下文)由于原始数组已排序,我们可以使用k-way merge algorithm在A
时间内创建数组B
和O(n*logk)
(一个例子是min-heap:at the开始将所有原始数组的第一个元素放在最小堆中;然后迭代地弹出最小堆中的最小元素,放入B
,并从堆中的相同原始数组中推送下一个元素。因此,对于您提供的示例,数组A
和B
将如下所示:A = [5, 6, 7, 11, 18], B = [1, 2, 1, 0, 2]
(5
来自第二个原始数组,6
来自第三个原始阵列等。)。
现在我们使用sliding windows technique来查找大小至少为k
的窗口,其中last和first元素之间的差异最小。我们的想法是遍历数组B
,直到我们从所有原始数组中“收集”元素 - 这意味着我们找到了一个组合,现在只检查第一个和最后一个元素之间的差异这种组合。数组C
现在出现在游戏中 - 我们用-1初始化它的所有元素,并将C[i]
设置为原始数组i
中任何元素的最后一个索引。一旦我们找到包含来自所有原始数组的元素的第一个滑动窗口,我们进一步将该窗口向右扩展并从左侧收缩,同时保持所有原始数组的代表都在窗口内的属性。因此,算法将如下所示:
// create arrays A and B, initialize array C
int collected = 0;
int min_idx = 0;
int result = INT_MAX;
for (int i = 0; i < n; ++i) {
bool check_result = false;
if (C[B[i]] == -1) {
++collected;
check_result = true;
}
C[B[i]] = i;
while (min_idx < C[B[min_idx]] && min_idx < i) {
check_result = true;
++min_idx;
}
if (collected < k) continue;
if (check_result && result > (A[i] - A[min_idx]))
result = (A[i] - A[min_idx]);
}
return result;
让我们通过你的例子来解释它:
A = [5, 6, 7, 11, 18]
B = [1, 2, 1, 0, 2]
C = [-1, -1, -1]
i = 0 // state after step 0, we have seen element from array 1
min_idx = 0
C = [-1, 0, -1]
collected = 1
result = INT_MAX
i = 1 // we have seen element from array 2
min_idx = 0
C = [-1, 0, 1]
collected = 2
result = INT_MAX
i = 2 // again element from array 1, increase min_idx
min_idx = 1
C = [-1, 2, 1]
collected = 2
result = INT_MAX
i = 3 // element from array 0, window is full, update result
min_idx = 1
C = [3, 2, 1]
collected = 3
result = 5
i = 4 // again element from array 2, increase min_idx and compare with result -> it is bigger, so don't update result
min_idx = 2
C = [3, 2, 4]
collected = 3
result = 5
时间复杂度为O(n*logk)
,因为从A
排序数组创建数组B
和k
需要O(n*logk)
,并且在循环期间每个n
}元素最多检查两次,因此该部分为O(n)
,最后为O(n*logk + n) = O(n*logk)
。如果将原始数组合并为一个,则为the best you can get:
可以证明不存在基于比较的k-way合并算法 在O(n f(k))中运行时间,其中f渐近渐渐变慢 而不是对数。
希望这有帮助!