簡體   English   中英

k個排序數組中元素的O(n)時間最小跨度窗口組合

[英]O(n) time smallest spanning window combination of the elements in k sorted arrays

有沒有一種方法可以在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 ,因為該組合的差異最小。

如果有幫助,原始數組的整個值集中都沒有重復的元素。

將數字以及每個數組分別排序:

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)

TL; DR我認為這不可能在O(n) (盡管我無法證明),因此在下面可以找到兩個O(n*logk)解決方案。

解決方案1:使用尺寸的最小堆k如所描述的在這里

解決方案2 (我的)

我們需要3個額外的數組,我們稱它們為ABC 我們也將輸入數組稱為“原始數組”。

  • A大小為n ,並將所有原始數組中的所有元素按排序順序保留
  • B也將具有大小n和將保持關於元素的陣列的源信息A ,即,從在原始數組元素A來自
  • C大小為k ,並且將包含該過程中原始數組中元素的最后看到的索引(請參見下文)

由於原始數組已排序,因此我們可以使用k合並算法O(n*logk)時間內創建數組AB (一個示例是使用min-heap:在開始時,將所有原始數組的第一個元素放入min堆;然后從最小堆中彈出最小的元素,放入B ,然后從堆中相同原始數組中推送下一個元素)。 因此,對於您提供的示例,數組AB如下所示: A = [5, 6, 7, 11, 18], B = [1, 2, 1, 0, 2]5來自第二個原始數組陣列,其中6來自第三個原始陣列等)。

現在,我們使用滑動窗口技術找到大小至少為k的窗口,該窗口的最后一個元素與第一個元素之間的差異最小。 這個想法是遍歷數組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)因為從k排序的數組中創建數組AB需要O(n*logk) ,並且在循環期間最多要檢查兩次n元素,因此這部分是O(n) ,最后O(n*logk + n) = O(n*logk) 如果將原始數組合並為一個,這將是最好的

可以證明,在O(nf(k))中,運行時間不存在基於比較的k向合並算法,其中f的漸近速度比對數慢。

希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM