常用演算法之回溯法

爬蜥發表於2018-12-27

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

示例

組合總和:給定一個無重複元素的陣列 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;
    }
複製程式碼

附錄

回溯思路總結

相關文章