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:
- The array is divided into exactly
kparts - Each part is a contiguous subarray (elements stay in their original order)
- Among all possible ways to split the array into
kparts, 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.
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 (whenk = 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 Implementation
1class Solution:
2 def splitArray(self, nums: List[int], k: int) -> int:
3 def feasible(max_sum):
4 """
5 Check if we can split the array into at most k subarrays
6 where each subarray sum doesn't exceed max_sum.
7 """
8 current_sum = 0
9 subarray_count = 1 # Start with one subarray
10
11 for num in nums:
12 if current_sum + num > max_sum:
13 # Start a new subarray
14 current_sum = num
15 subarray_count += 1
16 else:
17 current_sum += num
18
19 return subarray_count <= k
20
21 # Binary search bounds
22 left, right = max(nums), sum(nums)
23 first_true_index = -1
24
25 while left <= right:
26 mid = (left + right) // 2
27 if feasible(mid):
28 first_true_index = mid
29 right = mid - 1
30 else:
31 left = mid + 1
32
33 return first_true_index
341class Solution {
2 public int splitArray(int[] nums, int k) {
3 // Initialize binary search bounds
4 int left = 0;
5 int right = 0;
6
7 for (int num : nums) {
8 left = Math.max(left, num);
9 right += num;
10 }
11
12 int firstTrueIndex = -1;
13
14 while (left <= right) {
15 int mid = left + (right - left) / 2;
16 if (feasible(nums, mid, k)) {
17 firstTrueIndex = mid;
18 right = mid - 1;
19 } else {
20 left = mid + 1;
21 }
22 }
23
24 return firstTrueIndex;
25 }
26
27 private boolean feasible(int[] nums, int maxSum, int k) {
28 int currentSum = 0;
29 int subarrayCount = 1;
30
31 for (int num : nums) {
32 if (currentSum + num > maxSum) {
33 currentSum = num;
34 subarrayCount++;
35 } else {
36 currentSum += num;
37 }
38 }
39
40 return subarrayCount <= k;
41 }
42}
431class Solution {
2public:
3 int splitArray(vector<int>& nums, int k) {
4 int left = 0, right = 0;
5 for (int& num : nums) {
6 left = max(left, num);
7 right += num;
8 }
9
10 auto feasible = [&](int maxSum) {
11 int currentSum = 0;
12 int subarrayCount = 1;
13
14 for (int& num : nums) {
15 if (currentSum + num > maxSum) {
16 currentSum = num;
17 subarrayCount++;
18 } else {
19 currentSum += num;
20 }
21 }
22
23 return subarrayCount <= k;
24 };
25
26 int firstTrueIndex = -1;
27
28 while (left <= right) {
29 int mid = left + (right - left) / 2;
30 if (feasible(mid)) {
31 firstTrueIndex = mid;
32 right = mid - 1;
33 } else {
34 left = mid + 1;
35 }
36 }
37
38 return firstTrueIndex;
39 }
40};
411function splitArray(nums: number[], k: number): number {
2 let left: number = Math.max(...nums);
3 let right: number = nums.reduce((acc, num) => acc + num, 0);
4
5 const feasible = (maxSum: number): boolean => {
6 let currentSum: number = 0;
7 let subarrayCount: number = 1;
8
9 for (const num of nums) {
10 if (currentSum + num > maxSum) {
11 currentSum = num;
12 subarrayCount++;
13 } else {
14 currentSum += num;
15 }
16 }
17
18 return subarrayCount <= k;
19 };
20
21 let firstTrueIndex: number = -1;
22
23 while (left <= right) {
24 const mid: number = Math.floor((left + right) / 2);
25 if (feasible(mid)) {
26 firstTrueIndex = mid;
27 right = mid - 1;
28 } else {
29 left = mid + 1;
30 }
31 }
32
33 return firstTrueIndex;
34}
35Solution Approach
The solution uses binary search combined with a greedy validation function to find the minimum possible largest sum. We apply the standard binary search template to find the first value where splitting is feasible.
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) - Initialize
first_true_index = -1to track the answer
Feasible Function:
The feasible(max_sum) function determines if we can split the array into at most k subarrays where no subarray sum exceeds max_sum. This creates a boolean pattern over our search space:
max_sum: 10 11 12 ... 17 18 19 ... 32 feasible: false false false ... false true true ... true
We want to find the first true position.
The checking logic:
- Initialize
current_sum = 0andsubarray_count = 1(we start with one subarray) - For each element in
nums:- If adding the element exceeds
max_sum, start a new subarray - Otherwise, add it to the current subarray
- If adding the element exceeds
- Return
subarray_count <= k
Binary Search Template:
left, right = max(nums), sum(nums)
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if feasible(mid):
first_true_index = mid
right = mid - 1 # Try to find a smaller valid value
else:
left = mid + 1 # Current value is too small
return first_true_index
Why This Works:
- If a maximum sum
mxallows splitting into at mostksubarrays, we record it and try smaller values - If a maximum sum
mxrequires more thanksubarrays, we need to increasemx - Binary search efficiently finds the minimum value where splitting is 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 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [7,2,5,10,8] and k = 2.
Step 1: Set up binary search
left = max(nums) = 10(minimum possible largest sum)right = sum(nums) = 32(maximum possible largest sum)first_true_index = -1(tracks the answer)
Step 2: Binary search with template
Iteration 1: left=10, right=32
mid = (10 + 32) // 2 = 21- Check
feasible(21):current_sum = 0,count = 1- Add 7:
current_sum = 7 - Add 2:
current_sum = 9 - Add 5:
current_sum = 14 - Add 10:
14 + 10 = 24 > 21, start new subarray:current_sum = 10,count = 2 - Add 8:
current_sum = 18 - Result:
count = 2 ≤ k = 2→True
feasible(21) = True, sofirst_true_index = 21,right = 20
Iteration 2: left=10, right=20
mid = (10 + 20) // 2 = 15- Check
feasible(15):- After processing: needs 3 subarrays (14, 10, 8)
- Result:
count = 3 > k = 2→False
feasible(15) = False, soleft = 16
Iteration 3: left=16, right=20
mid = (16 + 20) // 2 = 18- Check
feasible(18):- Subarrays:
[7,2,5](sum=14),[10,8](sum=18) - Result:
count = 2 ≤ k = 2→True
- Subarrays:
feasible(18) = True, sofirst_true_index = 18,right = 17
Iteration 4: left=16, right=17
mid = (16 + 17) // 2 = 16feasible(16) = False(needs 3 subarrays)left = 17
Iteration 5: left=17, right=17
mid = 17feasible(17) = False(needs 3 subarrays)left = 18
Loop ends: left = 18 > right = 17
Answer: first_true_index = 18
The final split: [7,2,5] (sum = 14) and [10,8] (sum = 18).
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
checkfunction uses variabless,cnt, andxwhich requireO(1)space - The binary search uses variables
leftandrightwhich requireO(1)space - The
bisect_leftfunction with a range object doesn't create the entire range in memory but generates values on-the-fly, usingO(1)space
Therefore, the space complexity is O(1).
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common mistake is using while left < right with right = mid instead of the standard template with first_true_index.
Incorrect:
while left < right: mid = (left + right) // 2 if feasible(mid): right = mid else: left = mid + 1 return left
Correct (template-compliant):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index
The template approach is more explicit about tracking the answer and handles edge cases consistently.
2. Incorrect Binary Search Bounds
A common mistake is setting incorrect bounds:
- Using
left = 0orleft = min(nums)instead ofleft = max(nums) - The minimum possible maximum sum is the largest single element (each subarray must have at least one element)
Why this matters: If nums = [10, 5, 13] and k = 3, the answer should be 13. Using left = 0 would waste iterations on invalid values.
3. Confusion Between "At Most k" vs "Exactly k"
The feasible function checks for "at most k subarrays" (subarray_count <= k), not exactly k.
Incorrect:
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 to create exactly k subarrays without increasing the maximum sum.
4. Greedy Algorithm Correctness
The greedy approach (starting a new subarray only when sum exceeds limit) is optimal for a fixed maximum sum limit. Delaying the split allows more elements per subarray, minimizing the total count.
How many ways can you arrange the three letters A, B and C?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Want a Structured Path to Master System Design Too? Don’t Miss This!