![](/img/trans.png)
[英]Get k-smallest elements in range sorted using Sparse Table in O(klogk)
[英]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個額外的數組,我們稱它們為A
, B
和C
我們也將輸入數組稱為“原始數組”。
A
大小為n
,並將所有原始數組中的所有元素按排序順序保留 B
也將具有大小n
和將保持關於元素的陣列的源信息A
,即,從在原始數組元素A
來自 C
大小為k
,並且將包含該過程中原始數組中元素的最后看到的索引(請參見下文) 由於原始數組已排序,因此我們可以使用k合並算法在O(n*logk)
時間內創建數組A
和B
(一個示例是使用min-heap:在開始時,將所有原始數組的第一個元素放入min堆;然后從最小堆中彈出最小的元素,放入B
,然后從堆中相同原始數組中推送下一個元素)。 因此,對於您提供的示例,數組A
和B
如下所示: 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
排序的數組中創建數組A
和B
需要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.