2

I have been trying to solve this leetcode question Combination Sum

Given an array of distinct integers candidates and a target integer, >return a list of all unique combinations of candidates where the >chosen numbers sum to target. You may return the combinations in any >order.

The same number may be chosen from candidates an unlimited number of >times. Two combinations are unique if the frequency of at least one of >the chosen numbers is different.

And I came up with this solution:

class Solution {
    public void recUtil(List<List<Integer>> result, int[] candidates, List<Integer> list, int sum, int target, int ind) {
        if(sum>target || ind>=candidates.length) return;
        if(sum==target) {
            result.add(new ArrayList<>(list));
            return;
        }

        recUtil(result, candidates, list, sum, target, ind+1);
        list.add(candidates[ind]);
        recUtil(result, candidates, list, sum+candidates[ind], target, ind);
        list.remove(list.size()-1);
    }

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        
        recUtil(result, candidates, new ArrayList<>(), 0, target, 0);

        return result;        
    }
}

This solution does not work if I replace the 5th line result.add(new ArrayList<>(list)); with result.add(list);

For the input:

[2,3,6,7]

And target 7, I get two empty ArrayLists in the result ArrayList

[[],[]]

Instead of

[[2,2,3],[7]]

I know it must be something stupid, so I was afraid to ask here. I am new to java, can anyone please explain why just adding the list to the result does not work.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • You can't and shouldn't try to avoid duplicating the list. Without duplicating the list, the result will contain multiple references to the same list object, which is not what you want. – Eran Aug 16 '23 at 05:07
  • 1
    The copy is necessary to capture the state of `list` when you add it to `result`. Otherwise `list.add()` and `list.remove()` would continue modifying it in place. – shmosel Aug 16 '23 at 05:07

3 Answers3

2

This solution does not work if I replace the 5th line result.add(new ArrayList<>(list)); with result.add(list);

Correct. You seem already to understand that the latter adds the working list itself, not a copy of it, so you should also understand that in that case, the subsequent invocations of list.add() and list.remove() affect the same list that you added to the result, however many times you added it. That's not what you want. To be able to return multiple solutions, you need multiple lists, just as your original code provides.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

Elaborating to what @shmosel pointed out, if you do: result.add(list); you are directly adding the list to the result. So, when you do: list.add(candidates[ind]); and list.remove(list.size()-1); later on, it will modify the list and consequently the result list as well. It is because, the list you have stored inside the result and the one you are modifying later on with add and remove is the same list.

So, to make sure that it does not happen, you need to create a copy of the list and store it in the result instead of using the original list directly.

hermit
  • 1,048
  • 1
  • 6
  • 16
-1

The issue you're encountering is related to how Java handles objects and references. When you add the list to the result without creating a new instance of ArrayList, you're actually adding a reference to the same list object. As a result, when you later modify the list (by adding or removing elements), those changes are reflected in the previously added lists as well, since they all point to the same underlying object.

To fix this issue, you will need to create a new instance of ArrayList each time you want to add it to the result. This ensures that each combination is stored separately and not affected by future modifications. This is why the line result.add(new ArrayList<>(list)); works correctly.

In contrast, when you use result.add(list);, you're adding a reference to the existing list, and subsequent modifications will impact all the references to that list.

I hope that answers your question.

Below is my solution to Combination Sum problem that you're trying to solve

public class CombinationSum {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        backtrack(result, new ArrayList<>(), candidates, target, 0);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> current, int[] candidates, int target, int start) {
        if (target < 0) {
            return;
        } else if (target == 0) {
            result.add(new ArrayList<>(current));
        } else {
            for (int i = start; i < candidates.length; i++) {
                current.add(candidates[i]);
                backtrack(result, current, candidates, target - candidates[i], i);
                current.remove(current.size() - 1);
            }
        }
    }

    public static void main(String[] args) {
        CombinationSum solution = new CombinationSum();
        int[] candidates = {2, 3, 6, 7};
        int target = 7;
        List<List<Integer>> result = solution.combinationSum(candidates, target);
        System.out.println(result);
    }
}