18

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

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

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) + 额外的内存。

相关话题:

4

7 回答 7

3

一个有效的解决方案是基于这样一个事实,即大小为 k 的子数组(或窗口)的总和可以使用大小为 k 的先前子数组(或窗口)的总和在 O(1) 时间内获得。除了大小为 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)

资源

于 2019-08-26T15:12:53.300 回答
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)); 
} 

解释

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

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

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

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

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


资源

于 2019-07-13T03:56:12.527 回答
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);
        }
    }
于 2019-08-09T11:55:32.213 回答
0

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

因此,最复杂的解决方案包括 O(N²/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])

图形化:

在此处输入图像描述

于 2018-01-24T22:37:59.690 回答
0

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

于 2019-05-20T00:32:02.557 回答
0

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

于 2018-02-02T18:29:36.560 回答
-2

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

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

于 2015-06-01T09:50:16.190 回答