Alkesh

LeetCode - Combination Sum II

Problem statement

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.

Each number in candidates may only be used once in the combination.

Note: The solution set must not contain duplicate combinations.

Problem statement taken from: https://leetcode.com/problems/combination-sum-ii.

Example 1:

Input: candidates = [10, 1, 2, 7, 6, 1, 5], target = 8
Output:
[
[1, 1, 6],
[1, 2, 5],
[1, 7],
[2, 6]
]

Example 2:

Input: candidates = [2, 5, 2, 1, 2], target = 5
Output:
[
[1, 2, 2],
[5]
]

Constraints:

- 1 <= candidates.length <= 100
- 1 <= candidates[i] <= 50
- 1 <= target <= 30

Explanation

Backtracking

The problem can be solved using the similar approach we used in our last blog Combination Sum. The only difference is that each element in candidates may only be used once. Using each element only once, can be achieved by jumping to the next index when calling the function recursively.

Another difference is that our solution set must not contain duplicate combinations. The problem statement does not mention the array is sorted. To avoid duplicate result sets, we need to sort the array and avoid using the same elements.

Let's check the algorithm first.

- initialize the result as a 2D array
  initialize current as an array

  // n = index, at start it will be 0.
  // sumTillNow = sum of the current elements in the array, at the start it will be 0
  // current = current list of elements in the array, at the start it will be an empty array []
- call combinationSum2Util(result, candidates, current, n, sumTillNow, target)

- return result

// combinationSum2Util function
- if sumTillNow == target
  // append current to result
  - result.push_back(current)

- if sumTillNow > target
  - return

- set prev = -1

- loop for i = n; i <= candidates.size() - 1; i++
  - if prev != candidates[i]
    // append candidates array ith element to the current array
    - current.push_back(candidates[i])

    - sumTillNow += candidates[i]

    // call the function recursively
    - combinationSumUtil(result, candidates, i, target, sumTillNow, current)

    - sumTillNow -= current[current.size() - 1]

    // remove the last element from the array
    - current.pop_back()

    - prev = candidates[i]
  - end of if

Let's check out our solutions in C++, Golang, and Javascript.

C++ solution

class Solution {
public:
    void combinationSum2Util(vector<vector<int>> &result, vector<int>& candidates, vector<int>& current, int index, int sumTillNow, int target) {
        if(sumTillNow == target) {
            result.push_back(current);
            return;
        }

        if(sumTillNow > target) {
            return;
        }

        int prev = -1;

        for(int i = index; i <= candidates.size() - 1; i++) {
            if(prev != candidates[i]) {
                current.push_back(candidates[i]);
                sumTillNow += candidates[i];

                combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target);
                sumTillNow -= current[current.size() - 1];

                current.pop_back();
                prev = candidates[i];
            }
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        vector<int> current;

        sort(candidates.begin(), candidates.end());
        combinationSum2Util(result, candidates, current, 0, 0, target);
        return result;
    }
};

Golang solution

func combinationSum2Util(result *[][]int, candidates, current []int, index, sumTillNow, target int) {
    if sumTillNow == target {
        *result = append(*result, append([]int{}, current...))
        return
    }

    if sumTillNow > target {
        return
    }

    prev := -1

    for i := index; i < len(candidates); i++ {
        if prev != candidates[i] {
            current = append(current, candidates[i])
            sumTillNow += candidates[i]

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            sumTillNow -= current[len(current) - 1]
            current = current[:len(current) - 1]
            prev = candidates[i]
        }
    }
}

func combinationSum2(candidates []int, target int) [][]int {
    result := make([][]int, 0)

    sort.Ints(candidates)
    combinationSum2Util(&result, candidates, []int{}, 0, 0, target)

    return result
}

Javascript solution

var combinationSum2 = function(candidates, target) {
    let result = [];
    candidates.sort(function(a, b){ return a - b });

    const combinationSum2Util = (candidates, current, index, sumTillNow, target) => {
        if( sumTillNow == target ) {
            result.push([...current]);
            return;
        }

        if( sumTillNow > target ) {
           return;
        }

        let prev = -1;

        for(let i = index; i < candidates.length; i++) {
            if(prev != candidates[i]) {
                current.push(candidates[i]);
                sumTillNow += candidates[i];

                combinationSum2Util(candidates, current, i + 1, sumTillNow, target);

                sumTillNow -= current[current.length - 1];
                current.pop();

                prev = candidates[i];
            }
        }
    }

    combinationSum2Util(candidates, [], 0, 0, target);

    return result;
};

Let's dry run our algorithm.

Input: candidates = [2, 5, 2, 1, 2]
       target = 5

// combinationSum function
Step 1: vector<vector<int>> result
        vector<int> current

Step 2: sort(candidates.begin(), candidates.end()
        [1, 2, 2, 2, 5]

Step 3: combinationSum2Util(result, candidates, current, 0, 0, target)

// combinationSum2Util function
Step 4: if sumTillNow == target
           0 == 5
           false

        if sumTillNow > target
           0 > 5
           false

        prev = -1

        loop for int i = index; i <= candidates.size() - 1
          i = 0
          0 <= 5 - 1
          true

          - if prev != candidates[i]
               -1 != 1
               true

            current.push_back(candidates[i])
            current.push_back(candidates[0])
            current.push_back(1)
            current = [1]

            sumTillNow += candidates[i]
                        = sumTillNow + candidates[0]
                        = 0 + 1
                        = 1

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            combinationSum2Util([][], [1, 2, 2, 2, 5], [1], 1, 1, 5)

Step 5: if sumTillNow == target
           1 == 5
           false

        if sumTillNow > target
           1 > 5
           false

        prev = -1

        loop for int i = index; i <= candidates.size() - 1
          i = 1
          1 <= 5 - 1
          true

          - if prev != candidates[i]
               -1 != 2
               true

            current.push_back(candidates[i])
            current.push_back(candidates[1])
            current.push_back(2)
            current = [1, 2]

            sumTillNow += candidates[i]
                        = sumTillNow + candidates[1]
                        = 1 + 2
                        = 3

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            combinationSum2Util([][], [1, 2, 2, 2, 5], [1, 2], 3, 3, 5)

Step 6: if sumTillNow == target
           3 == 5
           false

        if sumTillNow > target
           3 > 5
           false

        prev = -1

        loop for int i = index; i <= candidates.size() - 1
          i = 2
          2 <= 5 - 1
          true

          - if prev != candidates[i]
               -1 != 2
               true

            current.push_back(candidates[i])
            current.push_back(candidates[1])
            current.push_back(2)
            current = [1, 2, 2]

            sumTillNow += candidates[i]
                        = sumTillNow + candidates[1]
                        = 3 + 2
                        = 5

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            combinationSum2Util([][], [1, 2, 2, 2, 5], [1, 2, 2], 3, 5, 5)

Step 7: if sumTillNow == target
           5 == 5
           true

           result.push_back(current)
           result = [[1, 2, 2]]

           return

           we backtrack to step 6 and continue

Step 8: combinationSumUtil([][], [1, 2, 2, 2, 5], [1, 2, 2], 3, 5, 5)
        sumTillNow = sumTillNow - current[current.size() - 1]
                   = 5 - current[3 - 1]
                   = 5 - current[2]
                   = 5 - 2
                   = 3

        current.pop_back()
        current = [1, 2]

        prev = candidates[i]
             = candidates[2]
             = 2

        i++
        i = 3

        loop for int i = index; i <= candidates.size() - 1
          i = 3
          3 <= 5 - 1
          true

          - if prev != candidates[3]
               2 != 2
               false

        i++
        i = 4

        loop for int i = index; i <= candidates.size() - 1
          i = 3
          4 <= 5 - 1
          true

          - if prev != candidates[4]
               2 != 5
               true

            current.push_back(candidates[i])
            current.push_back(candidates[4])
            current.push_back(5)
            current = [1, 2, 5]

            sumTillNow += candidates[i]
                        = sumTillNow + candidates[4]
                        = 3 + 5
                        = 8

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            combinationSum2Util([][], [1, 2, 2, 2, 5], [5], 5, 8, 5)

Step 9: if sumTillNow == target
           8 == 5
           false

        if sumTillNow > target
           8 > 5
           true
           return

        we backtrack to step 5 and continue

Step 10: combinationSum2Util([][], [1, 2, 2, 2, 5], [1, 2], 3, 3, 5)
         sumTillNow = sumTillNow - current[current.size() - 1]
                   = 3 - current[2 - 1]
                   = 3 - current[1]
                   = 3 - 2
                   = 1

         current.pop_back()
         current = [1]

         prev = candidates[i]
              = candidates[1]
              = 2

         i++
         i = 2

         loop for int i = index; i <= candidates.size() - 1
          i = 2
          2 <= 5 - 1
          true

          - if prev != candidates[3]
               2 != 2
               false

         i++
         i = 3

         loop for int i = index; i <= candidates.size() - 1
          i = 3
          3 <= 5 - 1
          true

          - if prev != candidates[3]
               2 != 2
               false

         i++
         i = 4

         loop for int i = index; i <= candidates.size() - 1
          i = 4
          4 <= 5 - 1
          true

          - if prev != candidates[4]
               2 != 5
               true

            current.push_back(candidates[i])
            current.push_back(candidates[4])
            current.push_back(5)
            current = [1, 5]

            sumTillNow += candidates[i]
                        = sumTillNow + candidates[4]
                        = 1 + 5
                        = 6

            combinationSum2Util(result, candidates, current, i + 1, sumTillNow, target)
            combinationSum2Util([][], [1, 2, 2, 2, 5], [1, 5], 5, 6, 5)

Step 11: if sumTillNow == target
           6 == 5
           false

         if sumTillNow > target
            6 > 5
            true
            return

         we backtrack to step 4 and continue

We similarly iterate from index 2, backtrack, and then reach the last index.

We return the answer as
[
  [1, 2, 2],
  [5]
]
Share this post!