Partition Equal Subset Sum

Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.
Example 1:
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
Below is a comprehensive, step-by-step guide to solve Partition Equal Subset Sum using Dynamic Programming, following the six-part approach we discussed.
1) Explain the Problem in Simple Terms
You’re given an array of non-negative integers, e.g. [1, 5, 11, 5]. You want to determine if it can be split into two subsets such that the sum of elements in both subsets is exactly the same.
For instance, the array
[1, 5, 11, 5]can be split into[1, 5, 5]and[11], each summing to 11.However,
[1, 2, 3, 5]cannot be split into two subsets of equal sum because the total sum is 11, and you can’t split 11 evenly.
Essentially, Partition Equal Subset Sum asks: “Does there exist a subset whose sum is exactly totalSum/2?” If totalSum is odd, the answer is immediately false.
2) Draw the Decision (Recursion) Tree
Consider a smaller example: nums = [1, 5, 5]. The total sum is 11, which is odd, so we’d quickly say false. But let’s pretend we check subsets anyway to illustrate a decision tree for a scenario where we attempt to form a target (say 5 or 6).
For a target T = 5 (just as a demonstration):
canForm(3, 5) // using first 3 items to form sum=5
/ \
Skip last(=5) Take last(=5)
/ \
canForm(2,5) canForm(2, 0)
... ...
canForm(i, s)means: “Can we form subset sumsusing the firstinumbers?”You either skip or take the
i-th number (if taking it doesn’t exceed the target).
In this problem, we specifically want s = totalSum/2 if totalSum is even.
3) Implement the Recursion + Memoization (Top-Down Approach)
Explanation
Check if totalSum is even. If it’s odd, return false immediately.
Otherwise, define
target = totalSum/2.Create a function
canPartition(i, remaining)that returns a boolean indicating whether we can formremainingusing the firstielements.If
remaining == 0, we found a valid subset → true.If
i == 0andremaining > 0, we don’t have more elements to use → false.We can skip the i-th element, or if
nums[i-1] <= remaining, we can also take it.
JavaScript Code (Recursion + Memo)
/**
* Partition Equal Subset Sum - Recursion + Memoization
* @param {number[]} nums - array of non-negative integers
* @return {boolean}
*/
function canPartitionMemo(nums) {
const totalSum = nums.reduce((a, b) => a + b, 0);
// If total sum is odd, can't split into two equal subsets
if (totalSum % 2 !== 0) {
return false;
}
const target = totalSum / 2;
const n = nums.length;
// Memo for (i, remaining) -> boolean
const memo = {};
function dfs(i, remaining) {
if (remaining === 0) return true; // Found a subset
if (i === 0) return false; // No items left but still needed sum
const key = `${i}-${remaining}`;
if (key in memo) {
return memo[key];
}
// Skip current item
let result = dfs(i - 1, remaining);
// Take current item if it fits
if (nums[i - 1] <= remaining) {
result = result || dfs(i - 1, remaining - nums[i - 1]);
}
memo[key] = result;
return result;
}
return dfs(n, target);
}
// Example usage:
console.log(canPartitionMemo([1,5,11,5])); // true
console.log(canPartitionMemo([1,2,3,5])); // false
4) Implement the Tabulation (Bottom-Up Approach)
Explanation
We can use a Subset Sum DP approach:
Let
target = totalSum / 2.Create a 2D boolean DP array:
dp[i][s] = can we form sumsusing the firstinumbers?Base cases:
dp[0][0] = true(sum of 0 with 0 items)dp[0][s] = falsefors > 0
Transition:
dp[i][s] = dp[i-1][s] OR (dp[i-1][s - nums[i-1]] if nums[i-1] <= s)At the end, check
dp[n][target].
JavaScript Code (Tabulation)
/**
* Partition Equal Subset Sum - Bottom-Up DP
* @param {number[]} nums
* @return {boolean}
*/
function canPartitionTab(nums) {
const totalSum = nums.reduce((acc, cur) => acc + cur, 0);
if (totalSum % 2 !== 0) return false;
const target = totalSum / 2;
const n = nums.length;
// dp[i][s] => can we form sum s using first i elements
const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(false));
// Base case: dp[0][0] = true
dp[0][0] = true;
for (let i = 1; i <= n; i++) {
const val = nums[i - 1];
for (let s = 0; s <= target; s++) {
// Option 1: skip
dp[i][s] = dp[i - 1][s];
// Option 2: take the number if it fits
if (s >= val && dp[i - 1][s - val]) {
dp[i][s] = true;
}
}
}
return dp[n][target];
}
// Example usage:
console.log(canPartitionTab([1,5,11,5])); // true
console.log(canPartitionTab([1,2,3,5])); // false
5) Optimize Space Complexity (If Possible)
We notice that each row dp[i][..] depends only on the previous row dp[i-1][..]. Therefore, we can reduce the 2D DP array into a 1D array of size target + 1.
However, we must iterate in descending order of s to avoid using the same element more than once.
JavaScript Code (Space-Optimized Tabulation)
/**
* Partition Equal Subset Sum - Space-Optimized DP
* @param {number[]} nums
* @return {boolean}
*/
function canPartitionTabOptimized(nums) {
const totalSum = nums.reduce((acc, val) => acc + val, 0);
if (totalSum % 2 !== 0) return false;
const target = totalSum / 2;
const dp = Array(target + 1).fill(false);
dp[0] = true; // We can always form sum=0 with an empty subset
for (const num of nums) {
// Go backward so we don't reuse the same element
for (let s = target; s >= num; s--) {
if (dp[s - num]) {
dp[s] = true;
}
}
}
return dp[target];
}
// Example usage:
console.log(canPartitionTabOptimized([1,5,11,5])); // true
console.log(canPartitionTabOptimized([1,2,3,5])); // false
Here, we only use O(target) space.
6) Analyze the Time Complexity
Let:
n = number of elements in
nums.S = sum of elements in
nums.Typically,
target = S/2.
Recursion + Memoization:
States:
(i, remaining), which can be at mostn * target=n * (S/2)= O(nS).Each state is computed once, and each transition is O(1).
Time Complexity: O(nS).
Space Complexity: O(nS) for the memo, plus up to O(n) recursion depth.
Tabulation (2D):
We fill a table of size
(n+1) x (target+1)→(n+1) x (S/2 + 1).Time Complexity: O(nS).
Space Complexity: O(nS).
Space-Optimized (1D):
Time Complexity is still O(nS), because for each of the
nelements, we potentially iterate fromtargetdown tonum.Space Complexity: O(S) (specifically
target + 1).
These complexities are typical for “Subset Sum” and “Partition Equal Subset Sum” type problems.
Final Recap
Problem: Can the array be split into two subsets of equal sum?
Decision Tree: For each element, skip or take it (exponential if naive).
Recursion + Memo: Use
(i, remaining)states to avoid repeated computations.Tabulation: Build a 2D DP table for subset-sum.
Space Optimization: Use a 1D rolling array in descending order of sums.
Complexities: O(nS) time, with either O(nS) or O(S) space.
This approach is standard for checking whether a subset with a given sum exists, which directly answers the Partition Equal Subset Sum question.




