The Maximum Sum Subarray Problem

In this problem, we will be given an array of integers, and we need to find the subarray with the maximum sum of all the possible subarrays of the given array.

Let us see an example to understand the problem.

Input: array = [-2, -3, 4, -1, -2, 1, 5, -3]

Output: 7

This array has the maximum sum of 7 all the subarrays.

Brute Force Approach

The simplest way to solve this problem is to calculate the sum of all the possible subarrays of the given array, compare them, and find the maximum possible sum. To solve this problem using this approach, we will need 3 loops. The first or the outermost loop will mark the beginning of each subarray. This loop will iterate over all the elements of the array. The second loop will mark the ending of the subarray. For each subarray, the ending index will equal n - 1, where n is the number of elements in the given array. The last loop will calculate the sum of each possible subarray between the starting and ending indexes marked by the outer two loops. While calculating the sum, we will continuously check for the maximum sum, and thus, in the end, we will get the maximum possible subarray sum.

These are the steps we will follow to solve this problem using the brute force approach:

  • Firstly, we will start a for loop running from index 0 to n - 1. Let's say the iterator of this loop is s. The s will be the starting index for the subarrays.
  • Inside the first loop, we will run another for loop to mark the ending of the subarray. For every subarray, the ending index will be n - 1. Let's say the iterator of this loop is e. This loop will run from the starting to the last possible ending index, hence from s to n - 1.
  • The last or the innermost loop will calculate the sum of each subarray. This loop will run from the starting index s to the ending e of the subarray. This loop will give us the sum of the subarray bounded by the index s and e
  • After completing this loop, we will check if the sum value of this loop is more than the maximum sum value till now. If yes, then we will update the maximum sum value.
  • At last, we will return the maximum sum.

Code

Output:

The maximum sum of a subarray is 7

Time Complexity: The time complexity of this approach will be cubic since we are using three for loops. Hence, the final time complexity is O(n^3), where n is the number of elements given in the array.

Space Complexity: We have not used any extra space in this approach; hence, the space complexity is constant, i.e., O(1).

Optimal Approach

The cubic time complexity is the major drawback of the brute force approach. However, we can optimize this approach slightly to decrease the time complexity from cubic to quadratic. As we can observe inside the third loop, we are calculating the sum of the following subarrays: array[s : s+1], array[s : s+2], array[s : s+3], and so on till array[s : e]. Hence, we are recalculating the sum for the previous subarray and then adding the current element. As sum(array[s : s+3] = sum(array[s : s+1]) + array[s+2].

These are the steps that we will follow:

  • We will start as we did in the previous approach. We will create a for loop that will iterate over every array element. The iterator s of this loop will mark the beginning of the subarray.
  • Initialize a variable that will store the sum of the subarray. Let this be summ and summ = 0
  • Then, we will start the second for loop that will run from s to n - 1. This will be the end of the subarray.
  • Inside the second loop, we need not to add another loop. We will add the current element to the sum, i.e., summ += array[e]. Then, at each iteration of the second loop, we will update max_sum for the sum of the current subarray.

Code

Output:

The maximum sum of a subarray is 7

Kadane's Algorithm

To solve this problem, we will use the very famous Kadane's Algorithm. In this algorithm, we will maintain two variables. The first variable will store the value of the maximum sum of the continuous subarray that will end at any particular index. We will call this variable max_end_here. The second variable will store the maximum sum of all the individual subarrays we have encountered so far. We will call this one as max_till_now. At every iteration, we will check if the value of max_end_here exceeds the value of max_till_now. If yes, then we will update the value of max_till_now. In this way, at the end of the loop, we will get the maximum sum that a subarray can have in the array.

To summarize the Kadane's algorithm in two sentences: -

  • We will neglect the subarray that has a negative sum. We will do this by starting the continuous array's sum from zero again. That is, we will declare max_end_here = 0.
  • We will continue to add the integers of the array until the sum is positive.

Neglecting the subarray with a negative sum is because, for example, the array is [2, 3, -6, 10, 1]. The subarray [2, 3, -6] has a negative sum -1. Now, the subarray [10, 1] has the sum of 11. This is clear now: there is no point in adding the subarray [2, 3, -6] to the subarray [10, 1] as it will only decrease the sum.

So this is our pseudocode:

1. Initializing Variables:

max_till_now = -float("inf")

max_end_here = 0

2. Traversing Over Each Item of the Array

(a) max_end_here = max_end_here + array[i]

(b) if (max_till_now < max_end_here)

max_till_now = max_end_here

(c) if (max_end_here < 0)

max_end_here = 0

return max_till_now

Let us understand the functioning using an example. We will dry run the code on an array, array = [-2, -4, 5, -1, -3, 1, 5, -2]

3. Initializing the Two Main Variables

max_till_now = -float("inf")

max_end_here = 0

4. Starting the Loop

for i = 0, array[0] = -2

max_end_here = max_end_here + (-2)

Setting the value of max_end_here = 0 since max_end_here < 0

and setting max_till_now = -2

for i=1, array[1] = -4

max_end_here = max_end_here + (-4)

Since max_end_here < 0 and max_till_now = -2, max_till_now will become -6

Setting the value of max_end_here = 0 since max_end_here < 0

for i = 2, array[2] = 4

max_end_here = max_end_here + (5)

max_end_here = 5

max_till_now is now set to 5 since the value of max_end_here is greater than max_till_now

max_till_now = 5

for i = 3, array[3] = -1

max_end_here = max_end_here + (-1)

max_end_here = 4

Not changing the value of max_till_now as it is greater than max_end_here

for i = 4, array[4] = -3

max_end_here = max_end_here + (-3)

max_end_here = 1

for i = 5, array[5] = 1

max_end_here = max_end_here + (1)

max_ending_here = 2

for i = 6, array[6] = 5

max_end_here = max_end_here + (5)

max_end_here = 7

max_till_now will now be set to 7 as the value of max_end_here is greater than max_till_now

max_till_now = 7

for i = 7, array[7] = -2

max_end_here = max_end_here + (-2)

max_end_here = 6

In the end, the value of max_till_now is our answer. Hence, the maximum sum of a subarray of the initial array is 7.

These are the steps which we need to follow:

  • We will initialize the two variables max_till_now = -float("inf") and max_end_here = 0.
  • We will then start a for loop from 0 to N - 1 index, and for every index i, we will follow these operations:
    • We will add the value of array[i] to max_end_here.
    • We will check if the value of max_end_here exceeds the value of max_till_now. If yes, then we will update the value of max_till_now.
    • If at this iteration the value of max_end_here < 0 we will declare the value of max_end_here = 0
  • At last, we will return the value of max_till_now.

Below is the Python code for the above algorithm.

Code

Output:

The maximum contiguous sum is 7

Time Complexity: We are using a linear loop to traverse over the elements of the array; hence, the time complexity is O(N)

Auxiliary Space: We have not used any extra memory except to store the variables; hence, the space complexity is constant, i.e., O(1).

Recursive Function for Kadane's Algorithm

The basic strategy of Kadane's algorithm is to use "Divide-and-Conquer". We will break the array into smaller subarrays when we implement this function using the recursive function. Then, we will recursively call the function to find the maximum sum for the subarray of each subarray. The last step is to combine the individual solutions of the subproblems and find the final answer. Thus finding the maximum sum of the subarray of any given array.

Now, we will see the algorithm to find the maximum sum of a subarray of any given integer array using the recursive function.

  • We will start by defining a function that will take the array as its parameter.
  • The first thing to do in a recursive function is to specify the base condition. In this case, the base condition will be the one in which the length of the array passed through the recursive function is 1. In this case, we will return the element of that array as it is the only maximum sum possible.
  • Now, we can start writing the recursive statements. The first job for a recursive state is to divide the supplied array into two halves. Therefore, we will find the middle index of the current array. Now, the problem is split into two sub-problems.
  • We will call the function recursively for the two halves of the array, i.e., array[ : m] and array[m : ]. These function calls will return the maximum possible sum of a subarray for these two arrays. These will be left_max and right_max respectively.
  • Then, we will iterate over the right half of the current array and keep track of the sum of each subarray that starts from the middle point and ends at each array index. This way, we can find the maximum sum of the array that crosses the middle element or includes the middle element. This will be the right_max_sum.
  • We will repeat the same procedure for the left half of the array. This time, we will loop starting from the middle element and reaching the array's beginning. This way, we can find the maximum sum of the subarray, including the middle element in the left half. This will be the left_max_sum.
  • Now, we must return the final answer for a particular recursion state. The final answer for the current array will be the maximum value out of the three sum values that are right_max, left_max, and cross_max_sum (cross_max_sum is the maximum sum of the subarray that starts at any index of the left half and ends at any index of the right half of the current array. We can calculate this value by adding the left_max_sum and right_max_sum, individually giving us the maximum sum of the subarray that crosses the middle element.)
  • When the recursion stack is exhausted, we will get our final answer or the maximum sum of any contiguous subarray of the given array.

Below is the Python program for the implementation of the above recursive algorithm:

Code

Output:

The maximum sum of a subarray is 7