O(n)时间最小的生成窗口组合k个排序数组中的元素

时间:2018-01-22 02:16:12

标签: arrays algorithm time-complexity big-o

有没有办法在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,因为该组合的差异是最小的。

如果它有帮助,原始数组中的整个值集中没有重复元素。

2 个答案:

答案 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个额外的阵列,我们称之为ABC。我们也将输入数组称为“原始数组”。

  • A的大小为n,并会按排序顺序保留所有原始数组中的所有元素
  • B也会有n大小,并会保留有关数组A中元素来源的信息,即A中的原始数组元素来自
  • C的大小为k,它将包含在此过程中原始数组中最后看到的元素索引(见下文)

由于原始数组已排序,我们可以使用k-way merge algorithmA时间内创建数组BO(n*logk)(一个例子是min-heap:at the开始将所有原始数组的第一个元素放在最小堆中;然后迭代地弹出最小堆中的最小元素,放入B,并从堆中的相同原始数组中推送下一个元素。因此,对于您提供的示例,数组AB将如下所示: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排序数组创建数组Bk需要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渐近渐渐变慢   而不是对数。

希望这有帮助!