给定一个可能包含正整数和负整数的一维数组,找到具有最大和的连续数字子数组的总和。
例如,如果给定数组是 {-2, -5, 6, -2, -3, 1, 5 , -6},则最大子数组总和为 7(请参阅突出显示的元素)。
最简单的方法是运行两个循环。外循环选择开始元素,内循环找到与外循环选择的第一个元素的最大可能总和,并将该最大值与整体最大值进行比较。最后,返回总体最大值。 Naive 方法的时间复杂度为 O(n^2)。
使用分而治之的方法,我们可以在 O(nLogn) 时间内找到最大的子数组和。以下是分而治之的算法。
- 将给定的数组分成两半
- 返回以下三个中的最大值
- 左半部分的最大子数组和(进行递归调用)
- 右半部分的最大子数组和(进行递归调用)
- 最大子数组和,使得子数组穿过中点
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 现场工作专业课程和学生竞争性编程现场课程。