Find Maximum Subarray Divide and Conquer in DSA Javascript - Time & Space Complexity
We want to understand how the time needed grows when using divide and conquer to find the maximum subarray.
How does splitting the problem affect the work done as the input size grows?
Analyze the time complexity of the following code snippet.
function maxCrossingSum(arr, left, mid, right) {
let sum = 0, leftSum = -Infinity;
for (let i = mid; i >= left; i--) {
sum += arr[i];
if (sum > leftSum) leftSum = sum;
}
sum = 0;
let rightSum = -Infinity;
for (let i = mid + 1; i <= right; i++) {
sum += arr[i];
if (sum > rightSum) rightSum = sum;
}
return leftSum + rightSum;
}
function maxSubArray(arr, left, right) {
if (left === right) return arr[left];
let mid = Math.floor((left + right) / 2);
let leftMax = maxSubArray(arr, left, mid);
let rightMax = maxSubArray(arr, mid + 1, right);
let crossMax = maxCrossingSum(arr, left, mid, right);
return Math.max(leftMax, rightMax, crossMax);
}
This code finds the maximum sum of a subarray by splitting the array into halves recursively and combining results.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls splitting the array into halves.
- How many times: Each level splits the array into two parts, creating about log n levels.
- Secondary operation: The maxCrossingSum function loops over parts of the array once per call.
- How many times: At each level, the crossing sum loops cover the entire array once in total.
At each level, the array is split into two halves, and the crossing sum loops cover the whole array once.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * log2(10) ≈ 33 |
| 100 | About 100 * log2(100) ≈ 664 |
| 1000 | About 1000 * log2(1000) ≈ 9966 |
Pattern observation: The work grows a bit faster than linear but much slower than quadratic, roughly n times log n.
Time Complexity: O(n log n)
This means the time needed grows a little more than directly with input size, because of splitting and combining steps.
[X] Wrong: "Since the array is split in half each time, the time is just O(log n)."
[OK] Correct: Each level still processes the whole array once in the crossing sum step, so the total work adds up to n times log n.
Understanding this time complexity helps you explain how divide and conquer balances splitting and combining work efficiently.
"What if the maxCrossingSum function was optimized to run in constant time? How would the time complexity change?"
Practice
Divide and Conquer approach to find the maximum subarray sum?Solution
Step 1: Understand divide and conquer strategy
The array is split into two halves recursively to simplify the problem.Step 2: Combine results from subproblems
Find max subarray in left half, right half, and crossing the middle, then take the maximum of these three.Final Answer:
Split array, find max left, right, and crossing sums, then combine -> Option AQuick Check:
Divide and conquer = split and combine max sums [OK]
- Thinking sorting helps find max subarray sum
- Ignoring the crossing middle subarray
- Assuming max subarray is always the largest single element
Solution
Step 1: Identify base case for recursion
When the array has only one element, that element itself is the max subarray sum.Step 2: Return the single element value
Return the element value directly as the max sum for that subarray.Final Answer:
Return the single element when array length is 1 -> Option AQuick Check:
Base case single element = element value [OK]
- Returning 0 instead of the element for single element array
- Returning sum of two elements as base case
- Not defining a base case causing infinite recursion
function maxCrossingSum(arr, left, mid, right) {
let sum = 0, leftSum = -Infinity;
for (let i = mid; i >= left; i--) {
sum += arr[i];
if (sum > leftSum) leftSum = sum;
}
sum = 0;
let rightSum = -Infinity;
for (let i = mid + 1; i <= right; i++) {
sum += arr[i];
if (sum > rightSum) rightSum = sum;
}
return leftSum + rightSum;
}
const arr = [2, -1, 3, -4, 5];
console.log(maxCrossingSum(arr, 0, 2, 4));Solution
Step 1: Calculate left sum from mid to left
Starting at index 2 (value 3), sum = 3; then index 1 (-1), sum=2; index 0 (2), sum=4. Max leftSum = 4.Step 2: Calculate right sum from mid+1 to right
Starting at index 3 (-4), sum = -4; index 4 (5), sum=1. Max rightSum = 1.Step 3: Add leftSum and rightSum
4 + 1 = 5.Final Answer:
5 -> Option DQuick Check:
Left max 4 + Right max 1 = 5 [OK]
- Adding wrong subarray sums crossing mid
- Mixing indices for left and right sums
- Using max element instead of max crossing sum
function maxSubArray(arr, left, right) {
if (left === right) return arr[left];
const mid = Math.floor((left + right) / 2);
const leftMax = maxSubArray(arr, left, mid - 1);
const rightMax = maxSubArray(arr, mid + 1, right);
const crossMax = maxCrossingSum(arr, left, mid, right);
return Math.max(leftMax, rightMax, crossMax);
}Solution
Step 1: Check recursive calls range
Left recursive call uses mid - 1, which excludes mid element incorrectly.Step 2: Correct left recursive call to include mid
Change left recursive call to maxSubArray(arr, left, mid) to include mid element.Final Answer:
Left recursive call should be maxSubArray(arr, left, mid) -> Option BQuick Check:
Include mid in left recursion [OK]
- Excluding mid element in recursive calls
- Returning 0 instead of element in base case
- Incorrect mid calculation causing infinite recursion
[-2, 1, -3, 4, -1, 2, 1, -5, 4], what is the maximum subarray sum found using divide and conquer?Solution
Step 1: Identify the max subarray
The subarray [4, -1, 2, 1] (indices 3-6) sums to 4 + (-1) + 2 + 1 = 6.Step 2: Check extending right
[4, -1, 2, 1, -5] = 1 < 6; [4, -1, 2, 1, -5, 4] = 5 < 6.Step 3: Check extending left
[-3, 4, -1, 2, 1] = 3 < 6; further left smaller.Final Answer:
6 -> Option CQuick Check:
Max subarray sum = 6 [OK]
- Miscomputing as 7 by skipping the -3
- Choosing sum 5 by including the end
- Picking single largest element 4
Ready to prove it?
Pass the test and this pattern moves unknown
