Given a collection of candidate numbers (candidates
) and a target number (target
), find all unique combinations in candidates
where the candidate numbers sums to target
.
Each number in candidates
may only be used once in the combination.
Note:
- All numbers (including
target
) will be positive integers. - The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8, A solution set is: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5, A solution set is: [ [1,2,2], [5] ]
LeetCode 39. Combination Sum —— 組合總和的升級版。還是想象成圖來幫助理解。和第39題相比本題有兩個變化。第一,本題有重復節點;第二,每個節點只能用一次,即沒有自環。結合對39代碼注釋的理解,稍稍更改即可得到本題的解題思路:
如何處理自環問題?每次搜索新路徑的時候都從其下一個節點開始,而不是從它本身開始;
如何處理去重問題?每次回溯的時候,剛剛被剔除的節點不能在任何時候再被重新加入到路徑上。如何處理這個“任何時候”呢?要么用map標記被剔除的節點直到路徑搜索結束,要么應用排序,將所有有相同出權值的節點都放到一起,這樣可以方便找到下一個出權值不同的節點。
想完這兩個不同點,這道題就解決了。詳見代碼注釋。
Java
class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> res = new ArrayList<>(); if (candidates == null || candidates.length == 0 || target < 0) return res; List<Integer> list = new ArrayList<>(); Arrays.sort(candidates); //排序,使得尋找相同出權值的節點變得容易 get(candidates, target, 0, list, res); return res; } private void get(int[] candidates, int target, int i, List<Integer> list, List<List<Integer>> res) { if (i > candidates.length || target < 0) return; //因為沒有自環,所以每次都是從下一個節點開始搜索,要添加一個條件判斷節點仍在圖中 if (target == 0) { //滿足條件,添加至結果集 res.add(new ArrayList<>(list)); return; } for (int p = i; p < candidates.length; p++) { list.add(candidates[p]); //添加節點到路徑 get(candidates, target - candidates[p], p+1, list, res); //因為沒有自環,所以每次搜索更新路徑權值后的下一個節點 list.remove(list.size()-1); //回溯,將當前節點從路徑中剔除 while (p < candidates.length - 1 && candidates[p] == candidates[p+1]) p++; //因為存在重復節點,所以已經被剔除的節點不能再被放回到路徑中 } } }