Facebook Pixel

410. Split Array Largest Sum

Problem Description

You are given an integer array nums and an integer k. Your task is to split the array nums into exactly k non-empty subarrays in a way that minimizes the largest sum among all the subarrays.

A subarray is a contiguous portion of the original array, meaning the elements must appear consecutively in their original order.

The goal is to find a splitting strategy where:

  1. The array is divided into exactly k parts
  2. Each part is a contiguous subarray (elements stay in their original order)
  3. Among all possible ways to split the array into k parts, you want the one where the maximum sum of any subarray is as small as possible

For example, if you have nums = [7,2,5,10,8] and k = 2, you could split it as:

  • [7,2,5] and [10,8] with sums 14 and 18, so the largest sum is 18
  • [7,2] and [5,10,8] with sums 9 and 23, so the largest sum is 23
  • [7] and [2,5,10,8] with sums 7 and 25, so the largest sum is 25

Among these options (and others), the first split gives the minimum possible largest sum of 18.

The function should return this minimized largest sum value.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that there's a relationship between the maximum allowed sum for subarrays and the number of subarrays we need to create. If we allow a larger maximum sum per subarray, we can fit more elements into each subarray, resulting in fewer total subarrays. Conversely, if we restrict the maximum sum to be smaller, we'll need more subarrays to accommodate all elements.

Think of it like packing items into boxes with a weight limit - the higher the weight limit per box, the fewer boxes you need.

This relationship is monotonic: as we increase the maximum allowed sum, the number of required subarrays either stays the same or decreases. This monotonic property is perfect for binary search.

We can establish bounds for our search:

  • The minimum possible largest sum is max(nums) - we can't go lower than this because at least one subarray must contain the largest element
  • The maximum possible largest sum is sum(nums) - this happens when we put all elements in a single subarray (when k = 1)

For any candidate value mid between these bounds, we can greedily check if it's possible to split the array into at most k subarrays where no subarray sum exceeds mid. We traverse the array, accumulating elements into the current subarray. When adding another element would exceed mid, we start a new subarray. If we can do this with k or fewer subarrays, then mid is a valid maximum sum.

Since we want the minimum possible value of this maximum sum, we use binary search to efficiently find the smallest mid that allows a valid split into at most k subarrays.

Solution Approach

The solution uses binary search combined with a greedy validation function to find the minimum possible largest sum.

Binary Search Setup:

  • Initialize left = max(nums) as the lower bound (smallest possible maximum sum)
  • Initialize right = sum(nums) as the upper bound (largest possible maximum sum)
  • Use bisect_left to find the leftmost position where our condition becomes True

Validation Function (check): The check(mx) function determines if we can split the array into at most k subarrays where no subarray sum exceeds mx:

  1. Initialize s = inf (starting with infinity ensures we immediately start a new subarray) and cnt = 0 (count of subarrays)
  2. For each element x in nums:
    • Add x to the current sum: s += x
    • If s > mx, we've exceeded our limit:
      • Start a new subarray with just x: s = x
      • Increment the subarray count: cnt += 1
  3. Return cnt <= k to check if we used at most k subarrays

Binary Search Logic: The expression left + bisect_left(range(left, right + 1), True, key=check) works as follows:

  • range(left, right + 1) creates our search space
  • key=check applies the validation function to each middle value
  • bisect_left finds the first position where check(mx) returns True
  • Adding left to the result gives us the actual value (since bisect_left returns an index starting from 0)

Why This Works:

  • If a maximum sum mx allows splitting into at most k subarrays, any larger maximum sum will also work (but won't be minimal)
  • If a maximum sum mx requires more than k subarrays, we need to increase mx
  • Binary search efficiently finds the boundary where we transition from "not possible" to "possible"

Time Complexity: O(n * log(sum(nums) - max(nums))) where n is the length of the array Space Complexity: O(1) as we only use a constant amount of extra space

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [7,2,5,10,8] and k = 2.

Step 1: Set up binary search bounds

  • left = max(nums) = 10 (minimum possible largest sum)
  • right = sum(nums) = 32 (maximum possible largest sum)
  • Search space: [10, 11, 12, ..., 32]

Step 2: Binary search process

Let's trace through a few iterations:

Check mid = 21 (midpoint of [10, 32])

  • Start with s = inf, cnt = 0
  • Process element 7: s = inf + 7 > 21, so start new subarray: s = 7, cnt = 1
  • Process element 2: s = 7 + 2 = 9 ≤ 21, continue
  • Process element 5: s = 9 + 5 = 14 ≤ 21, continue
  • Process element 10: s = 14 + 10 = 24 > 21, start new subarray: s = 10, cnt = 2
  • Process element 8: s = 10 + 8 = 18 ≤ 21, continue
  • Result: cnt = 2 ≤ k = 2 ✓ (Valid, but can we go lower?)

Check mid = 15 (searching lower half)

  • Start with s = inf, cnt = 0
  • Process element 7: s = inf + 7 > 15, so start new subarray: s = 7, cnt = 1
  • Process element 2: s = 7 + 2 = 9 ≤ 15, continue
  • Process element 5: s = 9 + 5 = 14 ≤ 15, continue
  • Process element 10: s = 14 + 10 = 24 > 15, start new subarray: s = 10, cnt = 2
  • Process element 8: s = 10 + 8 = 18 > 15, start new subarray: s = 8, cnt = 3
  • Result: cnt = 3 > k = 2 ✗ (Invalid, need higher max sum)

Check mid = 18 (between 15 and 21)

  • Start with s = inf, cnt = 0
  • Process element 7: s = inf + 7 > 18, so start new subarray: s = 7, cnt = 1
  • Process element 2: s = 7 + 2 = 9 ≤ 18, continue
  • Process element 5: s = 9 + 5 = 14 ≤ 18, continue
  • Process element 10: s = 14 + 10 = 24 > 18, start new subarray: s = 10, cnt = 2
  • Process element 8: s = 10 + 8 = 18 ≤ 18, continue
  • Result: cnt = 2 ≤ k = 2 ✓ (Valid)

Check mid = 17

  • Following similar logic, we'd find cnt = 3 > k = 2 ✗ (Invalid)

Step 3: Binary search finds the answer The binary search determines that 18 is the minimum value where we can split the array into at most 2 subarrays.

The final split would be: [7,2,5] (sum = 14) and [10,8] (sum = 18), giving us a maximum sum of 18.

Solution Implementation

1class Solution:
2    def splitArray(self, nums: List[int], k: int) -> int:
3        def can_split_with_max_sum(max_sum_limit):
4            """
5            Check if we can split the array into at most k subarrays
6            where each subarray sum doesn't exceed max_sum_limit.
7          
8            Args:
9                max_sum_limit: The maximum allowed sum for any subarray
10          
11            Returns:
12                True if we can split into at most k subarrays, False otherwise
13            """
14            current_sum = float('inf')  # Initialize to infinity to force first element into new subarray
15            subarray_count = 0
16          
17            for num in nums:
18                current_sum += num
19              
20                # If adding current number exceeds limit, start a new subarray
21                if current_sum > max_sum_limit:
22                    current_sum = num
23                    subarray_count += 1
24          
25            # Check if number of subarrays is within limit
26            return subarray_count <= k
27      
28        # Binary search bounds:
29        # Minimum possible max sum: largest single element (when k = len(nums))
30        # Maximum possible max sum: sum of all elements (when k = 1)
31        min_possible = max(nums)
32        max_possible = sum(nums)
33      
34        # Binary search for the minimum valid maximum subarray sum
35        # bisect_left finds the leftmost position where can_split_with_max_sum returns True
36        return min_possible + bisect_left(
37            range(min_possible, max_possible + 1), 
38            True, 
39            key=can_split_with_max_sum
40        )
41
1class Solution {
2    /**
3     * Splits an array into k subarrays to minimize the largest sum among all subarrays.
4     * Uses binary search on the answer space.
5     * 
6     * @param nums The input array to be split
7     * @param k The number of subarrays to split into
8     * @return The minimized largest sum among all subarrays
9     */
10    public int splitArray(int[] nums, int k) {
11        // Initialize binary search bounds
12        // left: minimum possible answer (max element in array)
13        // right: maximum possible answer (sum of all elements)
14        int left = 0;
15        int right = 0;
16      
17        for (int num : nums) {
18            left = Math.max(left, num);  // Find the maximum element
19            right += num;                 // Calculate total sum
20        }
21      
22        // Binary search for the minimum valid maximum sum
23        while (left < right) {
24            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
25          
26            // Check if we can split array into at most k subarrays 
27            // with each subarray sum <= mid
28            if (canSplit(nums, mid, k)) {
29                right = mid;  // Try to find a smaller maximum sum
30            } else {
31                left = mid + 1;  // Current mid is too small, increase it
32            }
33        }
34      
35        return left;
36    }
37  
38    /**
39     * Checks if the array can be split into at most k subarrays
40     * where each subarray sum does not exceed maxSum.
41     * 
42     * @param nums The input array
43     * @param maxSum The maximum allowed sum for each subarray
44     * @param k The maximum number of subarrays allowed
45     * @return true if valid split is possible, false otherwise
46     */
47    private boolean canSplit(int[] nums, int maxSum, int k) {
48        // Initialize with a large value to force first element into first subarray
49        int currentSum = 1 << 30;  // Large initial value (2^30)
50        int subarrayCount = 0;      // Number of subarrays needed
51      
52        for (int num : nums) {
53            currentSum += num;
54          
55            // If adding current element exceeds maxSum, start a new subarray
56            if (currentSum > maxSum) {
57                subarrayCount++;
58                currentSum = num;  // Start new subarray with current element
59            }
60        }
61      
62        // Check if number of subarrays needed is within limit
63        return subarrayCount <= k;
64    }
65}
66
1class Solution {
2public:
3    int splitArray(vector<int>& nums, int k) {
4        // Initialize binary search bounds
5        // left: minimum possible sum (largest single element)
6        // right: maximum possible sum (sum of all elements)
7        int left = 0, right = 0;
8        for (int& num : nums) {
9            left = max(left, num);
10            right += num;
11        }
12      
13        // Lambda function to check if we can split array into at most k subarrays
14        // with each subarray sum not exceeding maxSum
15        auto canSplit = [&](int maxSum) {
16            int currentSum = 0;  // Current subarray sum
17            int splitCount = 1;  // Number of subarrays (start with 1)
18          
19            for (int& num : nums) {
20                // If adding current number exceeds maxSum, start a new subarray
21                if (currentSum + num > maxSum) {
22                    currentSum = num;  // Start new subarray with current number
23                    ++splitCount;      // Increment split count
24                  
25                    // If we need more than k subarrays, this maxSum is too small
26                    if (splitCount > k) {
27                        return false;
28                    }
29                } else {
30                    currentSum += num;  // Add to current subarray
31                }
32            }
33          
34            return true;  // Can split with at most k subarrays
35        };
36      
37        // Binary search for the minimum largest sum
38        while (left < right) {
39            int mid = left + (right - left) / 2;
40          
41            if (canSplit(mid)) {
42                // If we can split with mid as max sum, try smaller value
43                right = mid;
44            } else {
45                // If we cannot split with mid as max sum, need larger value
46                left = mid + 1;
47            }
48        }
49      
50        return left;  // Minimum largest sum among all k subarrays
51    }
52};
53
1/**
2 * Splits an array into k subarrays such that the maximum sum among all subarrays is minimized
3 * @param nums - The input array of numbers to split
4 * @param k - The number of subarrays to create
5 * @returns The minimized largest sum among all k subarrays
6 */
7function splitArray(nums: number[], k: number): number {
8    // Left boundary: minimum possible max sum (largest single element)
9    let left: number = Math.max(...nums);
10  
11    // Right boundary: maximum possible max sum (sum of all elements)
12    let right: number = nums.reduce((accumulator: number, current: number) => accumulator + current, 0);
13
14    /**
15     * Checks if we can split the array into at most k subarrays 
16     * where each subarray sum doesn't exceed maxSum
17     * @param maxSum - The maximum allowed sum for each subarray
18     * @returns true if splitting is possible, false otherwise
19     */
20    const canSplit = (maxSum: number): boolean => {
21        let currentSum: number = 0;
22        let subarrayCount: number = 0;
23      
24        // Iterate through each number in the array
25        for (const num of nums) {
26            currentSum += num;
27          
28            // If current sum exceeds maxSum, start a new subarray
29            if (currentSum > maxSum) {
30                currentSum = num;
31                subarrayCount++;
32              
33                // If we need more than k subarrays, this maxSum is too small
34                if (subarrayCount === k) {
35                    return false;
36                }
37            }
38        }
39      
40        return true;
41    };
42
43    // Binary search to find the minimum possible maximum sum
44    while (left < right) {
45        const mid: number = Math.floor((left + right) / 2);
46      
47        if (canSplit(mid)) {
48            // If we can split with this max sum, try a smaller one
49            right = mid;
50        } else {
51            // If we cannot split with this max sum, we need a larger one
52            left = mid + 1;
53        }
54    }
55  
56    return left;
57}
58

Time and Space Complexity

Time Complexity: O(n × log m)

The algorithm uses binary search on the range [max(nums), sum(nums)]. The search space has a size of sum(nums) - max(nums) + 1, which is at most m (where m = sum(nums)). Binary search over this range takes O(log m) iterations.

For each binary search iteration, the check function is called, which iterates through all n elements of the array once, taking O(n) time.

Therefore, the overall time complexity is O(n × log m), where n is the length of the array and m is the sum of all elements in the array.

Space Complexity: O(1)

The algorithm only uses a constant amount of extra space:

  • The check function uses variables s, cnt, and x which require O(1) space
  • The binary search uses variables left and right which require O(1) space
  • The bisect_left function with a range object doesn't create the entire range in memory but generates values on-the-fly, using O(1) space

Therefore, the space complexity is O(1).

Common Pitfalls

1. Incorrect Binary Search Bounds

A common mistake is setting incorrect bounds for the binary search. Some developers might use:

  • left = 0 or left = min(nums) instead of left = max(nums)
  • This fails because each subarray must contain at least one element, so the minimum possible maximum sum is the largest single element in the array

Why this matters: If nums = [10, 5, 13] and k = 3, the answer should be 13 (each element in its own subarray). Using left = 0 or left = 5 would cause the binary search to check invalid values.

2. Off-by-One Error in Subarray Counting

The initialization current_sum = float('inf') and subarray_count = 0 might seem counterintuitive. A common mistake is to initialize:

current_sum = 0
subarray_count = 1  # Starting with one subarray

Why the original is correct: Starting with current_sum = inf ensures the first element immediately triggers a new subarray creation, making the counting logic consistent throughout the iteration. The alternative initialization requires special handling for the last subarray.

3. Confusion Between "At Most k" vs "Exactly k"

The validation function checks for "at most k subarrays" (subarray_count <= k), not exactly k. Some might incorrectly use:

return subarray_count == k

Why "at most" works: If we can split into fewer than k subarrays with a given maximum sum, we can always further split those subarrays to create exactly k subarrays without increasing the maximum sum. The binary search finds the minimum value where splitting into at most k subarrays is possible.

4. Greedy Algorithm Assumption

The greedy approach (always starting a new subarray when the sum exceeds the limit) works here, but developers might question why we don't need dynamic programming or backtracking.

Why greedy works: For a fixed maximum sum limit, the greedy strategy of creating a new subarray as late as possible (only when we must) minimizes the number of subarrays needed. This is optimal because delaying the split allows us to potentially fit more elements in each subarray.

Solution to Avoid These Pitfalls:

class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        def can_split(max_sum):
            # Clear naming and documentation
            """Returns True if array can be split into at most k subarrays 
               with each sum <= max_sum"""
          
            # Explicit initialization with clear intent
            subarrays_needed = 1  # We need at least one subarray
            current_sum = 0
          
            for num in nums:
                if current_sum + num > max_sum:
                    # Must start a new subarray
                    subarrays_needed += 1
                    current_sum = num
                  
                    # Early termination if we need too many subarrays
                    if subarrays_needed > k:
                        return False
                else:
                    current_sum += num
          
            return True
      
        # Validate input constraints
        if k > len(nums):
            return max(nums)  # Each element in its own subarray
      
        # Clear bound definitions with comments
        left = max(nums)   # Minimum: largest element (can't split an element)
        right = sum(nums)  # Maximum: entire array as one subarray
      
        # Standard binary search pattern
        while left < right:
            mid = left + (right - left) // 2
            if can_split(mid):
                right = mid  # Try to find smaller maximum
            else:
                left = mid + 1  # Need larger maximum
      
        return left

This alternative implementation makes the logic more explicit and easier to verify, avoiding the common pitfalls while maintaining the same time complexity.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How would you design a stack which has a function min that returns the minimum element in the stack, in addition to push and pop? All push, pop, min should have running time O(1).


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More