Data Structure and Algorithm
Algorithms
Dynamic Programming
Subset Sum Problem

Subset Sum Problem

Given a set of non-negative integers and a value sum, determine if there is a subset of the given set with sum equal to the given sum.


Examples

Example 1

  • Input set: [3, 34, 4, 12, 5, 2]
  • Target Sum: 9
  • Output: true
  • Explanation: There is a subset [4, 5] with sum 9.

Example 2

  • Input set: [3, 34, 4, 12, 5, 2]
  • Target Sum: 30
  • Output: false
  • Explanation: No subset sums up to 30.

Mathematical Substructure

Let isSubsetSum(set, n, sum) return true if there exists a subset of the first n elements that sums up to sum.

  1. If the last element is greater than the sum (set[n-1] > sum): We cannot include the last element in the subset. isSubsetSum(set, n, sum) = isSubsetSum(set, n-1, sum)
  2. If the last element is less than or equal to the sum (set[n-1] <= sum): We return true if either including it or excluding it gives the target sum: isSubsetSum(set, n, sum) = isSubsetSum(set, n-1, sum) || isSubsetSum(set, n-1, sum - set[n-1])

Base Cases

  • isSubsetSum(set, n, 0) = true (An empty subset always has a sum of 0).
  • isSubsetSum(set, 0, sum) = false (No elements left to pick, and sum > 0).

1. Recursive Solution (Naive)

function isSubsetSumRecursive(set, n, sum) {
    // Base Cases
    if (sum === 0) return true;
    if (n === 0) return false;
 
    // If last element is greater than sum, ignore it
    if (set[n - 1] > sum) {
        return isSubsetSumRecursive(set, n - 1, sum);
    }
 
    // Check if sum can be obtained by either including or excluding the last element
    return isSubsetSumRecursive(set, n - 1, sum) || 
           isSubsetSumRecursive(set, n - 1, sum - set[n - 1]);
}

2. Memoization Solution (Top-Down DP)

function isSubsetSumMemoization(set, n, sum) {
    // Create a 2D array initialized with null
    const memo = Array.from({ length: n + 1 }, () => new Array(sum + 1).fill(null));
 
    function helper(i, s) {
        if (s === 0) return true;
        if (i === 0) return false;
 
        if (memo[i][s] !== null) return memo[i][s];
 
        if (set[i - 1] > s) {
            memo[i][s] = helper(i - 1, s);
        } else {
            memo[i][s] = helper(i - 1, s) || helper(i - 1, s - set[i - 1]);
        }
 
        return memo[i][s];
    }
 
    return helper(n, sum);
}

3. Tabulation Solution (Bottom-Up DP)

We construct a 2D table dp of size (n+1) x (sum+1).

function isSubsetSumTabulation(set, n, sum) {
    const dp = Array.from({ length: n + 1 }, () => new Array(sum + 1).fill(false));
 
    // Base case: If sum is 0, then answer is true (empty subset)
    for (let i = 0; i <= n; i++) {
        dp[i][0] = true;
    }
 
    // Base case: If sum is not 0 and set is empty, then answer is false
    for (let j = 1; j <= sum; j++) {
        dp[0][j] = false;
    }
 
    // Fill the table in bottom-up manner
    for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= sum; j++) {
            if (set[i - 1] <= j) {
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - set[i - 1]];
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
 
    return dp[n][sum];
}

4. Space Optimized Tabulation (1D DP Array)

Since dp[i][j] only depends on the previous row dp[i-1], we can optimize the space to O(sum) by using a 1D array filled from right to left.

function isSubsetSumSpaceOptimized(set, n, sum) {
    // dp[j] will store if sum j is possible
    const dp = new Array(sum + 1).fill(false);
    dp[0] = true; // Base case: sum 0 is always possible
 
    for (let i = 0; i < n; i++) {
        // Traverse sum from right to left
        for (let j = sum; j >= set[i]; j--) {
            if (dp[j - set[i]] === true) {
                dp[j] = true;
            }
        }
    }
 
    return dp[sum];
}
 
// Driver code
const set = [3, 34, 4, 12, 5, 2];
const sum = 9;
const n = set.length;
console.log("Is there a subset with sum 9?", isSubsetSumSpaceOptimized(set, n, sum));
// Output: Is there a subset with sum 9? true

Complexity Analysis

  • Time Complexity: O(N * Sum)
    • Where N is the number of elements and Sum is the target sum.
  • Space Complexity:
    • O(N * Sum) for standard 2D Tabulation/Memoization.
    • O(Sum) for the Space-Optimized 1D array approach.