思路:在包含問題的解空間中,按照深度優先搜尋的策略,從根節點出發深度探索解空間樹,當探索到某一節點時,先判斷該節點是否包含問題的解,如果包含,就從該節點觸發繼續探索下去,如果不包含該節點的解,則逐層向其祖先節點回溯。
示例
組合總和:給定一個無重複元素的陣列 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。
candidates 中的數字可以無限制重複被選取。
說明: 所有數字(包括 target)都是正整數。 解集不能包含重複的組合。
假設輸入資料為 candidates = [2,3,5], target = 8
。可以做如下初步分析:
- 可以無限制重複被選取,比如4個2滿足條件
- 要找到所有的組合,也就是說要窮盡的去探索所有可能的情況
- 當資料本身大於8或者和已經超過8則沒有必要對接下來的資料做繼續探索了
參照回溯法的思路:按照深度優先的規則,這裡對“深度”的定義則是從第一元素開始,因為它可以被無限制的選取,那麼就可以一直累加知道它會超過目標值,然後進行回溯
去掉了超過目標值的節點
- 優先選取第一個元素進行重複獲取,這裡就是一直往深度探索
- 條件滿足後,開始執行回溯
- 可以計算得到它不滿足和為8這個條件,繼續回溯
- 當前分支的和仍然小於8,可以繼續往下探索
- 條件不滿足,進行回溯
- 仍然不滿足和為8的條件,繼續回溯
- 和小於8可以繼續沿著這個分支進行深度探索,發現一個滿足條件的解
- 僅接著開始下一次的分支嘗試,仍不滿足,這時就可以往相鄰節點回溯
到新的頭節點之後,繼續遵循深度優先的原則即可
程式碼實現
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//先排序,方便直接拋棄大的值
Arrays.sort(candidates);
//開始深度搜尋,從圖中第一個位置開始找
List<List<Integer>> rs = dst(0,target,candidates);
return rs;
}
public List<List<Integer>> dst(int start,int target,int[] candidates){
List<List<Integer>> rs = new ArrayList<>();
for(int i=start;i<candidates.length;i++){
int v=candidates[i];
if(v>target){
break;//不滿足條件,結束探索
}else if(v==target){
//剛好滿足解的條件,加入解集
rs.add(Arrays.asList(v));
} else{
//深度優先探索
List<List<Integer>> sc= dst(i,target-v,candidates);
for(List<Integer> s:sc){
//組合後續的結果
List<Integer> f = new ArrayList<>();
f.add(v);
f.addAll(s);
rs.add(f);
}
}
}
return rs;
}
複製程式碼