經常使用算法之回溯法

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

示例

組合總和:給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中全部能夠使數字和爲 target 的組合。數組

candidates 中的數字能夠無限制重複被選取。bash

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

假設輸入數據爲 candidates = [2,3,5], target = 8。能夠作以下初步分析:ui

  1. 能夠無限制重複被選取,好比4個2知足條件
  2. 要找到全部的組合,也就是說要窮盡的去探索全部可能的狀況
  3. 當數據自己大於8或者和已經超過8則沒有必要對接下來的數據作繼續探索了

參照回溯法的思路:按照深度優先的規則,這裏對「深度」的定義則是從第一元素開始,由於它能夠被無限制的選取,那麼就能夠一直累加知道它會超過目標值,而後進行回溯spa

去掉了超過目標值的節點code

  • 優先選取第一個元素進行重複獲取,這裏就是一直往深度探索

  • 條件知足後,開始執行回溯

  • 能夠計算獲得它不知足和爲8這個條件,繼續回溯

  • 當前分支的和仍然小於8,能夠繼續往下探索

  • 條件不知足,進行回溯

  • 仍然不知足和爲8的條件,繼續回溯

  • 和小於8能夠繼續沿着這個分支進行深度探索,發現一個知足條件的解

  • 僅接着開始下一次的分支嘗試,仍不知足,這時就能夠往相鄰節點回溯

到新的頭節點以後,繼續遵循深度優先的原則便可cdn

代碼實現

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;
    }
複製代碼

附錄

回溯思路總結htm

相關文章
相關標籤/搜索