每个k = 1..n的所有大小为k的子阵列的最大总和

时间:2015-06-01 09:43:20

标签: arrays algorithm data-structures dynamic-programming interval-tree

给定一个大小为n的数组,为每个k从1到n ,找到大小为k的连续子阵列的最大总和。

这个问题有一个明显的解决方案,时间复杂度为O(N 2 )和O(1)空间。 Lua代码:

array = {7, 1, 3, 1, 4, 5, 1, 3, 6}
n = #array

function maxArray(k)
    ksum = 0
    for i = 1, k do
        ksum = ksum + array[i]
    end
    max_ksum = ksum
    for i = k + 1, n do
        add_index = i
        sub_index = i - k
        ksum = ksum + array[add_index] - array[sub_index]
        max_ksum = math.max(ksum, max_ksum)
    end
    return max_ksum
end

for k = 1, n do
    print(k, maxArray(k))
end

有没有更低时间复杂度的算法?例如,O(N log N)+额外的内存。

相关主题:

7 个答案:

答案 0 :(得分:2)

一种有效的解决方案基于以下事实:可以使用大小为k的先前子数组(或窗口)的和在O(1)时间内获得大小为k的子数组(或窗口)的和。除了大小为k的第一个子数组外,对于其他子数组,我们通过删除最后一个窗口的第一个元素并添加当前窗口的最后一个元素来计算总和。

这是相同的实现

int maxSum(int arr[], int n, int k) 
{ 
// k must be greater 
if (n < k) 
{ 
   cout << "Invalid"; 
   return -1; 
} 

// Compute sum of first window of size k 
int res = 0; 
for (int i=0; i<k; i++) 
   res += arr[i]; 

// Compute sums of remaining windows by 
// removing first element of previous 
// window and adding last element of  
// current window. 
int curr_sum = res; 
for (int i=k; i<n; i++) 
{ 
   curr_sum += arr[i] - arr[i-k]; 
   res = max(res, curr_sum); 
} 

return res; 
 } 

时间复杂度:O(n) 辅助空间:O(1)

Source

答案 1 :(得分:0)

如果您不添加任何其他约束,我认为没有比O(N²)更有效的解决方案。换句话说,除了探索所有其他子阵列之外,没有其他方法可以确定你找到了最大和子阵列。

因此,最不复杂的解包含O(N 2/2),它是给定长度N的数组的连续子阵列的总数。

我个人会用动态编程方法来实现它。这个想法有一个部分结果的楔形,并使用它们来构建子阵列的当前总和(代替计算整个总和)。无论如何,这只“加速”,因此复杂度为O(N²/ 2)~O(N²)。

以下是伪代码 - 抱歉不说Lua

// here we place temporary results, row by row alternating in 0 or 1
int[2][N] sum_array_buffer
// stores the start of the max subarray
int[N] max_subarray_start
// stores the value
int[N] max_subarray_value

array = {7, 1, 3, 1, 4, 5, 1, 3, 6}
// we initialize the buffer with the array (ideally 1-length subarrays)
sum_array_buffer[1] = array
// the length of subarrays - we can also start from 1 if considered
for k = 1 ; k <= (N); ++k:
    // the starting position fo the sub-array
    for j = 0; j < (N-k+1); ++j:
        sum_array_buffer[k%2][j] = sum_array_buffer[(k+1)%2][j] + array[j+k-1]
        if j == 0 || sum_array_buffer[k%2][j] > max_subarray_value[k]:
            max_subarray_value = sum_array_buffer[k%2][j]
            max_subarray_start[k] = j


for k = 1 ; k <= (N); ++k:
    print(k, max_subarray_value[k])

Graphycally:

enter image description here

答案 2 :(得分:0)

该问题可以减少到最小程度的抽搐,请参阅https://core.ac.uk/download/pdf/84869149.pdf中的2.4(MCSP)节。因此,目前您可以预期的最佳复杂度可能是O(n ^ 2 / polylog(n))。

答案 3 :(得分:0)

int maxCrossingSum(int arr[], int l, int m, int h) 
{ 
    // Include elements on left of mid. 
    int sum = 0; 
    int left_sum = INT_MIN; 
    for (int i = m; i >= l; i--) 
    { 
        sum = sum + arr[i]; 
        if (sum > left_sum) 
          left_sum = sum; 
    } 

    // Include elements on right of mid 
    sum = 0; 
    int right_sum = INT_MIN; 
    for (int i = m+1; i <= h; i++) 
    { 
        sum = sum + arr[i]; 
        if (sum > right_sum) 
          right_sum = sum; 
    } 

    // Return sum of elements on left and right of mid 
    return left_sum + right_sum; 
} 

// Returns sum of maxium sum subarray in aa[l..h] 
int maxSubArraySum(int arr[], int l, int h) 
{ 
   // Base Case: Only one element 
   if (l == h) 
     return arr[l]; 

   // Find middle point 
   int m = (l + h)/2; 

   /* Return maximum of following three possible cases 
      a) Maximum subarray sum in left half 
      b) Maximum subarray sum in right half 
      c) Maximum subarray sum such that the subarray crosses the midpoint */
   return max(maxSubArraySum(arr, l, m), 
              maxSubArraySum(arr, m+1, h), 
              maxCrossingSum(arr, l, m, h)); 
} 

说明

  

使用分而治之方法,我们可以找到O(nLogn)时间中的最大子数组总和。以下是分而治之算法。

     

1)将给定数组分成两半

     

2)返回以下三个的最大值

     

.... a)左半部分的最大子数组总和(进行递归调用)

     

.... b)子数组总和的右半部分最大(进行递归调用)


source

答案 4 :(得分:0)

    The above question can be solved by O(n).
    Please try this algorithm.
    lets say k=3.
array = {7, 1, 3, 1, 4, 5, 1, 3, 6}
    maxsum=0.
    1)We start with adding 7+1+3 and store sum=11.since sum >maxsum.maxsum=11.
    2)Now since size of k=3,next continuous array is 1+3+1.so how we get this sum??
    remove 7 from sum and add 1 to sum.so now sum is 5.Check if sum>maxsum.
    3)Similarly do for other elements as well.This loop will run until (n-1).``

请在此处找到代码

 class Program
    {
        static void Main(string[] args)
        {
            int sum=0;
            int max=0;
            int size=9;
           string input="7, 1, 3, 1, 4, 5, 1, 3, 6";
           string[] values=input.Split(',');
           int length=values.Length;
           int k=size-1;
           for(int i=0;i<=k;i++)
           {
             sum=sum+int.Parse(values[i]);
             max=sum;
           }
           for(int j=0;k<length-1;j++)
           {
               ++k;
            sum=(sum-int.Parse(values[j]))+int.Parse(values[k]);
            if(sum>max)
            max=sum;
           }
           Console.WriteLine(max);
        }
    }

答案 5 :(得分:-1)

我们创建一个容量为k的Dequeue,它只存储k个元素的当前窗口的有用元素。如果元素在当前窗口中并且大于当前窗口中左侧的所有其他元素,则该元素很有用。我们逐个处理所有数组元素并保持Qi包含当前窗口的有用元素,这些有用的元素按排序顺序维护。 Qi前面的元素是最大的,Qi后面的元素是当前窗口中最小的元素。

答案 6 :(得分:-3)

以下流程可能对您有所帮助

1)选择前k个元素并创建一个大小为k的自平衡二进制搜索树(BST)。

2)运行i = 0到n - k

的循环

...... ..a)从BST获取最大元素并打印出来。

... ..b)在BST中搜索arr [i]并将其从BST中删除。

... ..c)将arr [i + k]插入BST。

时间复杂性: 步骤1的时间复杂度为O(kLogk)。步骤2(a),2(b)和2(c)的时间复杂度是O(Logk)。由于步骤2(a),2(b)和2(c)处于运行n-k + 1次的循环中,因此完整算法的时间复杂度为O(kLogk +(n-k + 1)* Logk)也可以写成O(nLogk)。