繁体   English   中英

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

[英]Maximum sum of all subarrays of size k for each k=1..n

给定大小为n的数组, 对于从1到n的每个k ,找到大小为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)+附加内存。

相关话题:

有效解决方案基于以下事实:使用大小为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)

资源

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)); 
} 

说明

使用Divide and Conquer方法,我们可以在O(nLogn)时间内找到最大子阵列总和。 以下是Divide and Conquer算法。

1)将给定的数组分成两半

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

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

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


资源

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

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

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

以下是伪代码 - 抱歉不说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:

在此输入图像描述

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

问题可以简化为最小和数,请参阅https://core.ac.uk/download/pdf/84869149.pdf中的第2.4节(MCSP)。 因此,目前您可以预期的最佳复杂度可能是O(n ^ 2 / polylog(n))。

    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);
        }
    }

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

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)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM