hard Split Array With Same Average
Problem Statement
You are given an array of integers nums.
You should insert each element of nums in either array A or B such that A and B are non-empty, and average(A) == average(B).
The average of arr is the sum of all the elements of arr divided by the length of arr.
Return true if it is possible to divide nums into two subarrays having the same average. Otherwise, return false.
Examples
-
Example 1:
- Input: nums =
[1, 5, 3, 5, 6] - Expected Output:
true - Explanation: We can split the array into A = [1, 5, 6] and B = [3, 5]. The average of both subarrays is 4.
- Input: nums =
-
Example 2:
- Input: nums =
[3, 5, 6, 4, 2, 10] - Expected Output:
true - Explanation: We can split the array into A = [6, 4] and B = [3, 5, 2, 10]. The average of both subarrays is 4.
- Input: nums =
-
Example 3:
- Input: nums =
[4, 8, 16, 24, 32, 40] - Expected Output:
false - Explanation: It's not possible to split the array into two subarrays with the same average.
- Input: nums =
Constraints:
- 1 <= nums.length <= 30
- 0 <= nums[i] <= 10 4
Try it yourself
Try solving this question here:
✅ Solution Split Array With Same Average
Problem Statement
You are given an array of integers nums.
You should insert each element of nums in either array A or B such that A and B are non-empty, and average(A) == average(B).
The average of arr is the sum of all the elements of arr divided by the length of arr.
Return true if it is possible to divide nums into two subarrays having the same average. Otherwise, return false.
Examples
-
Example 1:
- Input: nums =
[1, 5, 3, 5, 6] - Expected Output:
true - Explanation: We can split the array into A = [1, 5, 6] and B = [3, 5]. The average of both subarrays is 4.
- Input: nums =
-
Example 2:
- Input: nums =
[3, 5, 6, 4, 2, 10] - Expected Output:
true - Explanation: We can split the array into A = [6, 4] and B = [3, 5, 2, 10]. The average of both subarrays is 4.
- Input: nums =
-
Example 3:
- Input: nums =
[4, 8, 16, 24, 32, 40] - Expected Output:
false - Explanation: It's not possible to split the array into two subarrays with the same average.
- Input: nums =
Constraints:
- 1 <= nums.length <= 30
- 0 <= nums[i] <= 10 4
Solution
To solve this problem, we approach it by dividing the array into two halves and then generating all possible subsets for each half. This allows us to explore different ways to split the array into two non-empty subsets. The goal is to find two subsets, one from each half, such that the average of the elements in the two subsets is the same. To do this, we calculate the sum for each subset and use a mathematical relationship to determine if a corresponding subset exists in the other half. We leverage the fact that if the average of two subsets is the same, the equation involving their sums and sizes must hold true.
The algorithm first computes all possible subset sums for each half of the array. Then, it sorts the subset sums for one half to allow efficient searching using binary search. For each subset in the first half, the algorithm calculates the sum required in the second half to maintain the average equality. If such a sum exists in the sorted subset sums of the second half, the algorithm returns true. If no valid pair of subsets is found, it returns false.
Step-by-Step Algorithm
-
Input Check:
- Step 1: If the input array
numshas only one element, returnfalsebecause a single element cannot be split into two non-empty subsets. - Step 2: If the input array
numshas two elements, returntrueif both elements are equal, otherwise returnfalse.
- Step 1: If the input array
-
Initialize Variables:
- Step 3: Calculate
arrayLength, the total number of elements innums. - Step 4: Calculate
halfLength, which is half the length of the array, using integer division. This helps us divide the array into two parts. - Step 5: Calculate
totalSum, which is the sum of all elements in the arraynums. This sum will be used later to determine if a valid split is possible.
- Step 3: Calculate
-
Generate Subsets for Each Half:
- Step 6: Split the array
numsinto two parts:- First half: contains the first
halfLength + 1elements. - Second half: contains the remaining elements.
- First half: contains the first
- Step 7: Call the
generateSubsetSumsmethod on the first half to generate all possible subset sums for this part. The results are stored in a nested arrayfirstHalfSubsetSums, where each index represents the size of the subsets, and each list at that index stores the sums of all subsets of that particular size. - Step 8: Generate all possible subset sums for right half.
- Step 6: Split the array
-
Sort Subset Sums for the Second Half:
- Step 9: Sort each list in
secondHalfSubsetSumsin ascending order. Sorting is necessary because it allows us to perform an efficient binary search later when we need to check if a corresponding subset sum exists in the second half.
- Step 9: Sort each list in
-
Check for Matching Subsets:
- Step 10: Iterate through each subset sum in
firstHalfSubsetSumsusing two nested loops:- Outer Loop: The outer loop iterates through each possible subset size
iinfirstHalfSubsetSums. - Inner Loop: The inner loop iterates through each subset sum
sum1corresponding to sizei.
- Outer Loop: The outer loop iterates through each possible subset size
- Step 11: For each subset sum
sum1from the first half, iterate through each subset sum insecondHalfSubsetSumsusing another loop:- Skip Invalid Combinations: If the total subset size
i + jis either0(empty subset) or equalsnums.length(the entire array), skip to the next iteration because we need non-empty, proper subsets. - Calculate
requiredSum: For a valid split, calculate the required sum for the corresponding subset in the second half using the formula: - Step 12: If
requiredSumis not an integer (i.e., it has a fractional part), skip to the next iteration because only integer subset sums are possible. - Step 13: Perform a binary search in
secondHalfSubsetSums[j]to check ifrequiredSumexists in the second half's subsets of sizej. If found, returntrue.
- Skip Invalid Combinations: If the total subset size
- Step 10: Iterate through each subset sum in
-
Return Result:
- Step 14: If no valid subset pair is found after all iterations, return
false.
- Step 14: If no valid subset pair is found after all iterations, return
How RequiredSum Formula Works?
The formula used to calculate the requiredSum is:
This formula is derived from the condition that the average of the two subsets must be the same. Here's a step-by-step breakdown of how this formula works and how it helps determine the size and sum of the second subset.
Step-by-Step Derivation
-
Average Condition:
- Let
AandBbe two non-empty subsets of the original arraynums. - The average of subset
Ashould equal the average of subsetB:
- Let
-
Cross Multiplication:
- Cross multiplying the above equation gives:
- Rearranging this equation:
- Cross multiplying the above equation gives:
-
Total Sum Condition:
- The sum of all elements in the array
numsis: - Substituting
sum(B)from the previous step: - Rearranging this:
- The sum of all elements in the array
-
Simplifying the Expression:
- To solve for
sum(B), rearrange to: - This can be further simplified to:
- This is the
requiredSumwe use in the algorithm.
- To solve for
Algorithm Walkthrough
Let’s walk through each step of the algorithm in detail for the input nums = [1, 5, 3, 5, 6].
-
Initial Input Check:
- The array length is 5, which is greater than 2, so we proceed to the next steps.
-
Initialization:
arrayLength = 5halfLength = 2(since 5 // 2 = 2)totalSum = 1 + 5 + 3 + 5 + 6 = 20
-
Generate Subsets:
- Split
numsinto two parts:- First part:
[1, 5, 3] - Second part:
[5, 6]
- First part:
- Call
generateSubsetSumson[1, 5, 3]: - Subsets generated and their sums:
[]-> sum = 0 (size = 0)[1]-> sum = 1 (size = 1)[5]-> sum = 5 (size = 1)[3]-> sum = 3 (size = 1)[1, 5]-> sum = 6 (size = 2)[1, 3]-> sum = 4 (size = 2)[5, 3]-> sum = 8 (size = 2)[1, 5, 3]-> sum = 9 (size = 3)
- These are stored in the nested array:
subsetSums[0]=[0](subset with 0 elements)subsetSums[1]=[1, 5, 3](subsets with 1 element)subsetSums[2]=[6, 4, 8](subsets with 2 elements)subsetSums[3]=[9](subsets with 3 elements)
- Call
generateSubsetSumson[5, 6]:- Subsets generated and their sums:
[]-> sum = 0 (size = 0)[5]-> sum = 5 (size = 1)[6]-> sum = 6 (size = 1)[5, 6]-> sum = 11 (size = 2)
- These are stored in the nested array:
subsetSums[0]=[0](subset with 0 elements)subsetSums[1]=[5, 6](subsets with 1 element)subsetSums[2]=[11](subsets with 2 elements)
- Subsets generated and their sums:
- Split
-
Sort the Second Half Subsets:
- Sort each list in
secondHalfSubsetSums:[ [0], [5, 6], [11] ].
- Sort each list in
-
Check for Matching Subsets:
Step-by-Step Matching:
- For
i = 0,subsetSums[0] = [0]:- For
sum1 = 0:- For
j = 0,i + j = 0. So, continue to the next iteration. - For
j = 1,subsetSums[1] = [5, 6]:- Calculate
requiredSum=- 0 = 4 - 0 = 4. 4is not found in[5, 6], so continue.
- Calculate
- For
j = 2,subsetSums[2] = [11]:- Calculate
requiredSum=- 0 = 8 - 0 = 8. 8is not found in[11], so continue.
- Calculate
- For
- For
- For
i = 1,subsetSums[1] = [1, 5, 3]:- For
sum1 = 1:- For
j = 1,subsetSums[1] = [5, 6]:- Calculate
requiredSum=- 1 = 8 - 1 = 7. 7is not found in[5, 6], so continue.
- Calculate
- For
j = 2,subsetSums[2] = [11]:- Calculate
requiredSum=- 1 = 12 - 1 = 11. - Perform binary search on
[11], and11is found. - Return
trueas we found a valid pair.
- Calculate
- For
- For
- For
Code
import java.util.*;
public class Solution {
// Function to create all subsets of each half of the array
public List<Integer>[] generateSubsetSums(int[] arrayPart) {
int halfLength = arrayPart.length; // Get the length of the current half of the array
List<Integer>[] subsetSums = new List[halfLength + 1]; // Create an array of lists to store subset sums based on their size
// Initialize each list in the subsetSums array
for (int i = 0; i <= halfLength; i++) {
subsetSums[i] = new ArrayList<>();
}
// Generating all subsets using an iterative method (power set)
for (int mask = 0; mask < (1 << halfLength); mask++) {
int subsetSize = 0,
subsetSum = 0,
currentMask = mask,
index = 0;
while (currentMask != 0) {
if ((currentMask & 1) == 1) {
// Check if the lowest bit is 1 (part of the subset)
subsetSize++; // Increment the size of the current subset
subsetSum += arrayPart[index]; // Add the current element to the subset sum
}
index++;
currentMask >>= 1; // Shift right to process the next bit
}
subsetSums[subsetSize].add(subsetSum); // Add the sum of this subset to the corresponding list
}
return subsetSums; // Return the array of lists with all subset sums
}
public boolean splitArraySameAverage(int[] nums) {
int arrayLength = nums.length;
if (arrayLength == 1) return false; // A single element cannot be split into two subsets
if (arrayLength == 2) return nums[0] == nums[1]; // With two elements, they must be equal to split equally
int halfLength = arrayLength / 2; // Calculate the length of each half
int totalSum = Arrays.stream(nums).sum(); // Calculate the total sum of the array
// Generate subset sums for the first half and second half of the array
List<Integer>[] firstHalfSubsetSums = generateSubsetSums(
Arrays.copyOfRange(nums, 0, halfLength + 1)
);
List<Integer>[] secondHalfSubsetSums = generateSubsetSums(
Arrays.copyOfRange(nums, halfLength + 1, arrayLength)
);
// Sort each list in the second half subset for binary search later
for (int i = 0; i < halfLength; i++) {
Collections.sort(secondHalfSubsetSums[i]);
}
// Check if we can find two subsets (one from each half) that satisfy the condition
for (int i = 0; i < firstHalfSubsetSums.length; i++) {
for (int sum1 : firstHalfSubsetSums[i]) {
for (int j = 0; j < secondHalfSubsetSums.length; j++) {
// Skip cases where the subset size is 0 or equal to the total length
if (i + j == 0 || i + j == nums.length) continue;
// Calculate the required sum for the second subset
double requiredSum =
((1.0 * totalSum * (i + j)) / nums.length) - sum1;
if (Math.ceil(requiredSum) != requiredSum) continue; // Check if requiredSum is an integer
// Check if the required sum exists in the second half subsets using binary search
if (
Collections.binarySearch(
secondHalfSubsetSums[j],
(int) requiredSum
) >=
0
) {
return true; // If found, return true
}
}
}
}
return false; // If no valid subset found, return false
}
// Main method to test the code with provided examples
public static void main(String[] args) {
Solution solution = new Solution();
// Example 1
int[] nums1 = { 1, 5, 3, 5, 6 };
System.out.println("Example 1: " + solution.splitArraySameAverage(nums1));
// Example 2
int[] nums2 = { 3, 5, 6, 4, 2, 10 };
System.out.println("Example 2: " + solution.splitArraySameAverage(nums2));
// Example 3
int[] nums3 = { 4, 8, 16, 24, 32, 40 };
System.out.println("Example 3: " + solution.splitArraySameAverage(nums3));
}
}
Complexity Analysis
Time Complexity
- The main complexity arises from generating subsets for each half of the array. For an array of length
n, the subset generation for each half (n/2) will take O(2(n/2) * (n/2)) time. This is because we generate all possible subsets (which is 2(n/2)) and for each subset, we calculate its sum, which can take up toO(n/2)time. - Sorting each subset list for the second half takes O(2(n/2) * log(2(n/2)) which simplifies to O(2(n/2) * (n/2)).
- The final nested loop that checks for the matching subsets runs in O((2(n/2)2), which again simplifies to O((n)).
- Therefore, the overall time complexity is O(2(n) * (n/2)), where
nis the length of the input array.
Space Complexity
- The space complexity is primarily dominated by storing the subset sums for both halves of the array. Since we store all subset sums for
n/2elements, the space complexity is O(2(n/2)) for each half. - Therefore, the total space complexity is O(2(n/2) + 2(n/2)) = O(2(n/2))`.
🤖 Don't fully get this? Learn it with Claude
Stuck on Split Array With Same Average? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Split Array With Same Average** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Split Array With Same Average** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Split Array With Same Average**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Split Array With Same Average**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.