|
6 | 6 |
|
7 | 7 | /** Backtracking: pick/not-pick with reuse of candidates. */ |
8 | 8 | public class CombinationSum { |
| 9 | + private CombinationSum() { |
| 10 | + throw new UnsupportedOperationException("Utility class"); |
| 11 | +} |
| 12 | + |
| 13 | + public static List<List<Integer>> combinationSum(int[] candidates, int target) { |
| 14 | + List<List<Integer>> results = new ArrayList<>(); |
| 15 | + if (candidates == null || candidates.length == 0) { |
| 16 | + return results; |
| 17 | + } |
9 | 18 |
|
10 | | - public static List<List<Integer>> combinationSum(int[] candidates, int target) { |
11 | | - Arrays.sort(candidates); // helps pruning |
12 | | - List<List<Integer>> res = new ArrayList<>(); |
13 | | - backtrack(candidates, target, 0, new ArrayList<>(), res); |
14 | | - return res; |
15 | | - // Note: result order is not guaranteed; compare sorted in tests if needed |
| 19 | + // Sort to help with pruning duplicates and early termination |
| 20 | + Arrays.sort(candidates); |
| 21 | + backtrack(candidates, target, 0, new ArrayList<>(), results); |
| 22 | + return results; |
16 | 23 | } |
17 | 24 |
|
18 | | - private static void backtrack(int[] nums, int remain, int start, List<Integer> path, List<List<Integer>> res) { |
19 | | - if (remain == 0) { |
20 | | - res.add(new ArrayList<>(path)); |
| 25 | + private static void backtrack(int[] candidates, int remaining, int start, |
| 26 | + List<Integer> combination, List<List<Integer>> results) { |
| 27 | + if (remaining == 0) { |
| 28 | + // Found valid combination; add a copy |
| 29 | + results.add(new ArrayList<>(combination)); |
21 | 30 | return; |
22 | 31 | } |
23 | | - for (int i = start; i < nums.length; i++) { |
24 | | - int val = nums[i]; |
25 | | - if (val > remain) break; // prune |
26 | | - path.add(val); |
27 | | - backtrack(nums, remain - val, i, path, res); // i (reuse allowed) |
28 | | - path.remove(path.size() - 1); |
| 32 | + |
| 33 | + for (int i = start; i < candidates.length; i++) { |
| 34 | + int candidate = candidates[i]; |
| 35 | + |
| 36 | + // If candidate is greater than remaining target, further candidates (sorted) will also be too big |
| 37 | + if (candidate > remaining) { |
| 38 | + break; |
| 39 | + } |
| 40 | + |
| 41 | + // include candidate |
| 42 | + combination.add(candidate); |
| 43 | + // Because we can reuse the same element, we pass i (not i + 1) |
| 44 | + backtrack(candidates, remaining - candidate, i, combination, results); |
| 45 | + // backtrack: remove last |
| 46 | + combination.remove(combination.size() - 1); |
29 | 47 | } |
30 | 48 | } |
31 | 49 | } |
0 commit comments