簡體   English   中英

在線性時間內找到sum> = k的n個整數的最小子陣列

[英]Finding a minimal subarray of n integers of sum >= k in linear time

最近我一直在努力解決以下問題:

給定一個整數數組,找到一個總和至少為k的最小(最短長度)子數組。

顯然,這可以在O(n ^ 2)中輕松完成。 我能夠編寫一個算法,在自然數的線性時間內解決它,但我無法弄清楚它是否為整數。

我最近的嘗試是這樣的:

def find_minimal_length_subarr_z(arr, min_sum):
    found = False
    start = end = cur_end = cur_sum = 0
    for cur_start in range(len(arr)):
        if cur_end <= cur_start:
            cur_end, cur_sum = cur_start, arr[cur_start]
        else:
            cur_sum -= arr[cur_start-1]
        # Expand
        while cur_sum < min_sum and cur_end < len(arr)-1:
            cur_end += 1
            cur_sum += arr[cur_end]
        # Contract
        while cur_end > cur_start:
            new_sum = cur_sum - arr[cur_end]
            if new_sum >= min_sum or new_sum >= cur_sum:
                cur_end -= 1
                cur_sum = new_sum
            else:
                break
        if cur_sum >= min_sum and (not found or cur_end-cur_start < end-start):
            start, end, found = cur_start, cur_end, True
    if found:
        return start, end

例如:

[8, -7, 5, 5, 4], 12 => (2, 4)

但是,它失敗了:

[-12, 2, 2, -12, 2, 0], 4

其中正確的結果是(1, 2)但算法找不到它。

這可以在線性時間內完成(最好是空間復雜度恆定)嗎?

這是一個線性時間,也是線性空間。 額外的空間來自可以增長到線性尺寸的雙端隊列。 (還有第二個數組來維持累積總和,但這可以很容易地刪除。)

from collections import deque
def find_minimal_length_subarr(arr, k):
   # assume k is positive
   sumBefore = [0]
   for x in arr: sumBefore.append(sumBefore[-1] + x)
   bestStart = -1
   bestEnd = len(arr)
   startPoints = deque()
   start = 0
   for end in range(len(arr)):
      totalToEnd = sumBefore[end+1]
      while startPoints and totalToEnd - sumBefore[startPoints[0]] >= k: # adjust start
         start = startPoints.popleft()
      if totalToEnd - sumBefore[start] >= k and end-start < bestEnd-bestStart:
         bestStart,bestEnd = start,end
      while startPoints and totalToEnd <= sumBefore[startPoints[-1]]: # remove bad candidates
         startPoints.pop()
      startPoints.append(end+1) # end+1 is a new candidate
   return (bestStart,bestEnd)

雙端隊列從左到右保持一系列候選起始位置。 關鍵不變量是雙端隊列中的位置也通過增加“sumBefore”的值來排序。

為了理解原因,考慮兩個位置x和y,其中x> y,並假設sumBefore [x] <= sumBefore [y]。 那么x是比y更嚴格的起始位置(對於以x或更晚結尾的段),所以我們不需要再考慮y。

進一步解釋:

想象一個看起來像這樣的天真算法:

for end in 0..N-1
   for start in 0..end
      check the segment from start to end

我試圖改進內循環只考慮某些起點而不是所有可能的起點。 那么我們何時才能從進一步的考慮中消除一個特定的起點? 在兩種情況下。 考慮兩個起點S0和S1,其中S0位於S1的左側。

首先,如果我們發現S1開始符合條件的段(即,一個段總和至少為k),我們就可以消除S0。 這就是第一個while循環所做的,其中start是S0,startPoints [0]是S1。 即使我們從S0開始發現一些未來符合條件的段,它也會比我們從S1開始找到的段長。

其次,如果從S0到S1-1的元素之和<= 0(或等效地,如果S0之前的元素之和> = S1之前的元素之和),則可以消除S0。 這是第二個while循環所做的,其中S0是startPoints [-1]而S1是end + 1。 修剪從S0到S1-1的元素總是有意義的(對於S1或更高的終點),因為它會使段縮短而不減少其總和。

實際上,還有第三種情況我們可以消除S0:當從S0到end的距離大於到目前為止發現的最短段的長度。 我沒有實施這個案例,因為不需要它。

在這里,您可以使用偽代碼提供您正在尋找的解決方案。

curIndex = 0
while (curIndex <= endIndex)
{
    if(curSum == 0)
    {
        startIndex = curIndex
    }

    curSum = curSum + curVal
    curTot = curTot + 1
    if(curSum >= targetVal AND curTot < minTotSofar)
    { 
        maxSumSofar = curSum
        maxStartIndex = startIndex
        maxEndIndex = curIndex
        minTotSofar = curTot
        if(curTot == 1)
        {
            exit_loop
        }

        curSum = 0
        curTot = 0
        curIndex = startIndex   
    }
    else if(curIndex == endIndex)
    {
        if(maxSumSofar == 0 AND curSum >= targetValue)
        {
                maxSumSofar = curSum
                maxStartIndex = startIndex
                maxEndIndex = curIndex
                minTotSofar = curTot
         }
         else if(curSum < targetValue AND startIndex < endIndex)
         {
                curSum = 0
                curTot = 0
                curIndex = startIndex
         }
    }
    curIndex = curIndex + 1
}

------------ JWPAT7建議后的更新

INPUTS:整數數組,索引從0到endIndex 與( targetVal )進行比較的目標值(k)。

輸出:所選子集的最終添加( maxSumSoFar ),子集的起始索引( maxStartIndex ),子集的結束索引( maxEndIndex ),子集中的元素總數( minTotSofar )。

暫無
暫無

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

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