1. 程式人生 > >常用演算法之回溯法

常用演算法之回溯法

思路:在包含問題的解空間中,按照深度優先搜尋的策略,從根節點出發深度探索解空間樹,當探索到某一節點時,先判斷該節點是否包含問題的解,如果包含,就從該節點觸發繼續探索下去,如果不包含該節點的解,則逐層向其祖先節點回溯。

示例

組合總和:給定一個無重複元素的陣列 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。

candidates 中的數字可以無限制重複被選取。

說明: 所有數字(包括 target)都是正整數。 解集不能包含重複的組合。

假設輸入資料為 candidates = [2,3,5], target = 8

。可以做如下初步分析:

  1. 可以無限制重複被選取,比如4個2滿足條件
  2. 要找到所有的組合,也就是說要窮盡的去探索所有可能的情況
  3. 當資料本身大於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; } 複製程式碼

附錄

回溯思路總結