回溯算法--總結與例題整理

本文從以下幾個方面對回溯算法進行總結:

1、什麼是回溯算法

2、回溯算法與窮舉法的區別與聯繫

3、回溯算法的解題步驟(準備工作、解題步驟、遞歸方法的參數選擇)

4、例題:LeetCode-39 組合總和、LeetCode-40 組合總和Ⅱ

5、例題整理

一、什麼是回溯算法

        回溯算法(百度百科)實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。

二、回溯算法與窮舉法的區別與聯繫

        回溯法與窮舉法有某些聯繫,它們都是基於試探的,但是回溯算法是優於窮舉法的。窮舉法要將一個解的各個部分全部生成後,才檢查是否滿足條件,若不滿足,則直接放棄該完整解,然後再嘗試另一個可能的完整解,它並沒有沿着一個可能的完整解的各個部分逐步回退生成解的過程。而對於回溯法,一個解的各個部分是逐步生成的,當發現當前生成的某部分不滿足約束條件時,就放棄該步所做的工作,退到上一步進行新的嘗試,而不是放棄整個解重來。(所謂剪枝,也就是避免了一些不必要的搜索)

三、回溯算法的解題步驟

準備工作:

1)由於採用回溯法求解時存在退回到祖先結點的過程,所以需要保存搜索過的結點。

      通常有兩種方法:1、用子定義棧來保存;2、採用遞歸方法。

2)用回溯法通常採用兩種策略避免無效搜索。這兩類函數統稱爲剪枝函數

      1、用約束函數在擴展結點處剪除不滿足約束條件的路徑;

      2、用限界函數剪去得不到問題的解或最優解的路徑。

3)要保證遞歸函數返回 或 彈出棧頂元素 後,狀態可以恢復到操作前,以此達到真正的回溯。

 

回溯法求解的一般步驟:

1、針對給定的問題確定問題的解空間樹

2、確定結點的擴展搜索規則。

3、以深度優先方式搜索解空間樹,並在搜索過程中可以採用剪枝函數來避免無效搜索。其中,深度優先方式可以採用遞歸回溯或者非遞歸(迭代)回溯。


遞歸方法的參數選擇:

1、一個臨時變量(可以就直接傳遞一個字面量或者常量進去)傳遞不完整的解,因爲每一步選擇後,暫時還沒構成完整的解,這個時候這個選擇的不完整解,也要想辦法傳遞給遞歸函數。也就是,把每次遞歸的不同情況傳遞給遞歸調用的函數。
2、一個全局變量,用來存儲完整的每個解,一般是個集合容器(也不一定要有這樣一個變量,因爲每次符合結束條件,不完整解就是完整解了,直接打印即可)。
3、最重要的一點,一定要在參數設計中,可以得到結束條件。一個選擇是可以傳遞一個量n,也許是數組的長度,也許是數量,等等。

四、例題

LeetCode-39 組合總和 題目鏈接:https://leetcode-cn.com/problems/combination-sum/

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

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

說明:

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

示例 1:

輸入: candidates = [2,3,6,7],target = 7,
所求解集爲:
[
  [7],
  [2,2,3]
]

示例 2:

輸入: candidates = [2,3,5],target = 8,
所求解集爲:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);
        combinationSum(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    // 回溯算法
    // 1、題目爲無重複數組,故不需去重
    // 2、數字可被無限重複選取,故從start本身開始遞歸
    public static void combinationSum(int[] candidates, int target, int start, List<Integer> temp, List<List<Integer>> result) {
        if (target == 0) {
            result.add(new ArrayList<>(temp));
            return;
        }
        if (target < 0) {
            return;
        }
        for (int i = start; i < candidates.length && candidates[i] <= target; i++) {
            temp.add(candidates[i]);
            combinationSum(candidates, target - candidates[i], i, temp, result);
            temp.remove(temp.size()-1);
        }
    }

LeetCode-40 組合總和Ⅱ 題目鏈接:https://leetcode-cn.com/problems/combination-sum-ii/

給定一個數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和爲 target 的組合。

candidates 中的每個數字在每個組合中只能使用一次

說明:

  • 所有數字(包括目標數)都是正整數。
  • 解集不能包含重複的組合。 

示例 1:

輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集爲:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

輸入: candidates = [2,5,2,1,2], target = 5,
所求解集爲:
[
  [1,2,2],
  [5]
]
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);
        combinationSum2(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    // 回溯算法
    // 1、題目爲重複數組,故需要去重
    // 2、數字只能選取一次,故從start+1開始遞歸
    public static void combinationSum2(int[] candidates, int target, int start, List<Integer> temp, List<List<Integer>> result) {
        if (target < 0) {
            return;
        }
        if (target == 0) {
            result.add(new ArrayList<>(temp));
            return;
        }
        for (int i = start; i < candidates.length && candidates[i] <= target; i++) {
            if (i > start && candidates[i] == candidates[i-1]) // 去重
                continue;
            temp.add(candidates[i]);
            combinationSum2(candidates, target - candidates[i], i+1, temp, result);
            temp.remove(temp.size()-1);
        }
    }

五、例題整理

1、LeetCode-39 組合總和 題目鏈接:https://leetcode-cn.com/problems/combination-sum/

2、LeetCode-40 組合總和Ⅱ 題目鏈接:https://leetcode-cn.com/problems/combination-sum-ii/

3、LeetCode-46 全排列 題目鏈接:https://leetcode-cn.com/problems/permutations/

4、LeetCode-47 全排列Ⅱ 題目鏈接:https://leetcode-cn.com/problems/permutations-ii/

5、LeetCode-78 子集 題目鏈接:https://leetcode-cn.com/problems/subsets/

6、LeetCode-90 子集Ⅱ 題目鏈接:https://leetcode-cn.com/problems/subsets-ii/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章