📜  使用分而治之算法的最大子数组和

📅  最后修改于: 2021-09-16 11:14:59             🧑  作者: Mango

给定一个可能包含正整数和负整数的一维数组,找到具有最大和的连续数字子数组的总和。

例如,如果给定数组是 {-2, -5, 6, -2, -3, 1, 5 , -6},则最大子数组总和为 7(请参阅突出显示的元素)。

最简单的方法是运行两个循环。外循环选择开始元素,内循环找到与外循环选择的第一个元素的最大可能总和,并将该最大值与整体最大值进行比较。最后,返回总体最大值。 Naive 方法的时间复杂度为 O(n^2)。

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

  1. 将给定的数组分成两半
  2. 返回以下三个中的最大值
    • 左半部分的最大子数组和(进行递归调用)
    • 右半部分的最大子数组和(进行递归调用)
    • 最大子数组和,使得子数组穿过中点

2.a 和 2.b 行是简单的递归调用。如何找到最大子数组和以使子数组穿过中点?我们可以很容易地在线性时间内找到交叉和。思路很简单,求从中点开始到mid左边某点的最大和,然后从mid+1开始到mid+1右边的和点找到最大和,最后将两者结合起来并返回左,右和两者组合中的最大值。

下面是上述方法的实现:

C++
// A Divide and Conquer based program for maximum subarray
// sum problem
#include 
#include 
 
// A utility function to find maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }
 
// A utility function to find maximum of three integers
int max(int a, int b, int c) { return max(max(a, b), c); }
 
// Find the maximum possible sum in arr[] auch that arr[m]
// is part of it
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
    // returning only left_sum + right_sum will fail for
    // [-2, 1]
    return max(left_sum + right_sum, left_sum, right_sum);
}
 
// Returns sum of maximum 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));
}
 
/*Driver program to test maxSubArraySum*/
int main()
{
    int arr[] = { 2, 3, 4, 5, 7 };
    int n = sizeof(arr) / sizeof(arr[0]);
    int max_sum = maxSubArraySum(arr, 0, n - 1);
    printf("Maximum contiguous sum is %d\n", max_sum);
    getchar();
    return 0;
}


Java
// A Divide and Conquer based Java
// program for maximum subarray sum
// problem
import java.util.*;
 
class GFG {
 
    // Find the maximum possible sum in arr[]
    // such that arr[m] is part of it
    static int maxCrossingSum(int arr[], int l, int m,
                              int h)
    {
        // Include elements on left of mid.
        int sum = 0;
        int left_sum = Integer.MIN_VALUE;
        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 = Integer.MIN_VALUE;
        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
        // returning only left_sum + right_sum will fail for
        // [-2, 1]
        return Math.max(left_sum + right_sum,
                        Math.max(left_sum, right_sum));
    }
 
    // Returns sum of maximum sum subarray
    // in aa[l..h]
    static 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 Math.max(
            Math.max(maxSubArraySum(arr, l, m),
                     maxSubArraySum(arr, m + 1, h)),
            maxCrossingSum(arr, l, m, h));
    }
 
    /* Driver program to test maxSubArraySum */
    public static void main(String[] args)
    {
        int arr[] = { 2, 3, 4, 5, 7 };
        int n = arr.length;
        int max_sum = maxSubArraySum(arr, 0, n - 1);
 
        System.out.println("Maximum contiguous sum is "
                           + max_sum);
    }
}
// This code is contributed by Prerna Saini


Python
# A Divide and Conquer based program
# for maximum subarray sum problem
 
# Find the maximum possible sum in
# arr[] auch that arr[m] is part of it
 
 
def maxCrossingSum(arr, l, m, h):
 
    # Include elements on left of mid.
    sm = 0
    left_sum = -10000
 
    for i in range(m, l-1, -1):
        sm = sm + arr[i]
 
        if (sm > left_sum):
            left_sum = sm
 
    # Include elements on right of mid
    sm = 0
    right_sum = -1000
    for i in range(m + 1, h + 1):
        sm = sm + arr[i]
 
        if (sm > right_sum):
            right_sum = sm
 
    # Return sum of elements on left and right of mid
    # returning only left_sum + right_sum will fail for [-2, 1]
    return max(left_sum + right_sum, left_sum, right_sum)
 
 
# Returns sum of maximum sum subarray in aa[l..h]
def maxSubArraySum(arr, l, h):
 
    # Base Case: Only one element
    if (l == h):
        return arr[l]
 
    # Find middle point
    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))
 
 
# Driver Code
arr = [2, 3, 4, 5, 7]
n = len(arr)
 
max_sum = maxSubArraySum(arr, 0, n-1)
print("Maximum contiguous sum is ", max_sum)
 
# This code is contributed by Nikita Tiwari.


C#
// A Divide and Conquer based C#
// program for maximum subarray sum
// problem
using System;
 
class GFG {
 
    // Find the maximum possible sum in arr[]
    // such that arr[m] is part of it
    static int maxCrossingSum(int[] arr, int l, int m,
                              int h)
    {
        // Include elements on left of mid.
        int sum = 0;
        int left_sum = int.MinValue;
        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.MinValue;
        ;
        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
        // returning only left_sum + right_sum will fail for
        // [-2, 1]
        return Math.Max(left_sum + right_sum,
                        Math.Max(left_sum, right_sum));
    }
 
    // Returns sum of maximum sum subarray
    // in aa[l..h]
    static 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 Math.Max(
            Math.Max(maxSubArraySum(arr, l, m),
                     maxSubArraySum(arr, m + 1, h)),
            maxCrossingSum(arr, l, m, h));
    }
 
    /* Driver program to test maxSubArraySum */
    public static void Main()
    {
        int[] arr = { 2, 3, 4, 5, 7 };
        int n = arr.Length;
        int max_sum = maxSubArraySum(arr, 0, n - 1);
 
        Console.Write("Maximum contiguous sum is "
                      + max_sum);
    }
}
 
// This code is contributed by vt_m.


PHP
= $l; $i--)
    {
        $sum = $sum + $arr[$i];
        if ($sum > $left_sum)
        $left_sum = $sum;
    }
 
    // Include elements on right of mid
    $sum = 0;
    $right_sum = PHP_INT_MIN;
    for ($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
    // returning only left_sum + right_sum will fail for [-2, 1]
    return max($left_sum + $right_sum, $left_sum, $right_sum);
}
 
// Returns sum of maximum sum
// subarray in aa[l..h]
function maxSubArraySum(&$arr, $l, $h)
{
    // Base Case: Only one element
    if ($l == $h)
        return $arr[$l];
     
    // Find middle point
    $m = intval(($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));
}
 
// Driver Code
$arr = array(2, 3, 4, 5, 7);
$n = count($arr);
$max_sum = maxSubArraySum($arr, 0, $n - 1);
echo "Maximum contiguous sum is " . $max_sum;
 
// This code is contributed by rathbhupendra, Aadil
?>


Javascript


输出
Maximum contiguous sum is 21n

时间复杂度: maxSubArraySum() 是一种递归方法,时间复杂度可以表示为以下递归关系。
T(n) = 2T(n/2) + Θ(n)
上面的递归类似于归并排序,可以使用递归树方法或主方法来解决。它属于主方法的情况 II,递归的解决方案是 Θ(nLogn)。

这个问题的 Kadane 算法需要 O(n) 时间。因此,Kadane 算法比分而治之方法要好,但是这个问题可以被认为是展示分而治之威力的一个很好的例子。上面我们将数组分成两半的简单方法将时间复杂度从 O(n^2) 降低到 O(nLogn)。

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程