簡單算法:回溯法 例題:Leetcode 17 Leetcode 39

回溯算法 以及Leetcode例題

    回溯算法,正如其名所述,前進到某個地方能夠折返到交叉路再選擇另外一條路繼續前進。相似咱們數據結構中的樹。大部分回溯問題都離不開幾個元素:遞歸、深度優先以及全排列。如下是筆者做爲小萌新,以爲比較不錯的能夠用來理解回溯的思想的例題,特此摘記。git

題目以下:算法

給定一個僅包含數字 2-9 的字符串,返回全部它能表示的字母組合。數組

給出數字到字母的映射以下(與電話按鍵相同)。注意 1 不對應任何字母。數據結構

鍵盤圖

示例:函數

input: "2,3"code

output:[「ad」, 「ae」, 「af」, 「bd」, 「be」, 「bf」, 「cd」, 「ce」, 「cf」]blog

思路:遞歸

    拿到題目的時候,有些比較明顯的特徵在提示筆者此題應該是要用回溯的:rem

  • 題目的輸出近似是一種將知足條件全部排列都枚舉出來
  • 每次固定一個位置改變其餘位置

    與其餘的回溯問題有些不一樣之處的是,若是使用的是局部字符串變量保存解集元素的話,此題在遞歸結束的時候能夠不須要回溯。(詳細見Code)字符串

public class LetteCombination {
    /**
     * 用來存儲各個按鍵對應的字母
     */
    private List<String> letters = new ArrayList<String>();

    /**
     * 將按鍵對應的字母保存進letters
     */
    public void prepareForList(){
        letters.add("abc");
        letters.add("def");
        letters.add("ghi");
        letters.add("jkl");
        letters.add("mno");
        letters.add("pqrs");
        letters.add("tuv");
        letters.add("wxyz");
    }

    /**
     * 入口函數
     * @param digits 輸入字符串
     * @return
     */
    public List<String> letterCombinations(String digits) {
        if(0==digits.length()||digits==null){
            return new ArrayList<String>();
        }
        prepareForList();// 記得進行數據的初始化
        List<String> res = new ArrayList<>();
        letterCombinations(digits,0,"",res);
        return res;
    }

    /**
     * 選取這些參數的是爲了人爲控制每次遞歸的值傳遞,digits 和 res 是必須的
     * @param digits 輸入的按鍵
     * @param index 指向當前遍歷到的digits的元素
     * @param tmp 存放當前字母組合的結果
     * @param res 存放全部組合的結果
     */
    public void letterCombinations(String digits,int index,String tmp,List<String> res){
        // tmp達到預期長度就將tmp保存進res中
        if(tmp.length()==digits.length()){
            res.add(tmp);
            return;
        }
        // 讀取當前digits遍歷到的元素 對應的可選字符集
        String choose = letters.get(digits.charAt(index)-'2');
        for(int i=0;i<choose.length();i++){// 遍歷可選字符集,每次固定當前位置的字符,再進行下一位的字符選擇
            // 筆者認爲最核心的一句,相比於其餘的回溯須要進行還原操做,這裏使用局部變量的方式就不須要還原了
            String curr = tmp+choose.charAt(i);// 這裏遇到坑了,還原原始字符串,不比數組,字符串沒有remove方法。必須利用物理棧進行變量的個階段保存,即還原
            letterCombinations(digits,index+1,curr,res);// 遞歸
        }
    }

    public static void main(String[] args){
        String input = "23";
        LetteCombination lc = new LetteCombination();
        List<String> l = lc.letterCombinations(input);
        for(String s:l){
            System.out.print(l+"\t");
        }
    }
}

題目2以下:

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

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

說明:

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

示例 1:

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

思路:

    與上面的題目特徵類似,都是獲取可枚舉的排列中知足特定條件的值(此處是須要獲取加載之List中的元素的和等於target的這些List)。所以幾乎能夠說是直接照搬上面的回溯部分,稍做修改便可使用了。

    惟一須要注意的地方就是,爲了去重(如:不去重的話獲得的結果集多是:[2,2,3],[2,3,2]這樣的),咱們須要引入一個index做爲每次遍歷的起點,這樣保證每個解集元素List都是一個遞增的List,也就不會出現重複的現象了。

private List<List<Integer>> result = new ArrayList<List<Integer>>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        myMethod(candidates,target,new ArrayList<>(),0,0);
        return result;
    }

    /**
     *
     * @param candidates 可選元素集
     * @param target 目標大小
     * @param curr 解集
     * @param sum 記錄當前curr中全部元素之和
     * @param index 記錄上一次遞歸candidates使用的元素下標,例如:curr:2,3 說明index應該到了1
     */
    public void myMethod(int[] candidates,int target,List<Integer> curr,int sum,int index){
        // 達到目標大小則將curr加入結果List中
        if(sum == target){
            result.add(new ArrayList<>(curr));
            return;
        }
        // 大於目標大小就不必接着計算了直接返回
        else if(sum>target)
            return;
        // 起點的選擇筆者認爲是此題的核心,由於筆者剛開始i從0開始,會出現重複的例如:2,2,3 和 2,3,2,
        // 若 i 從 index 開始則會維持輸入元素的一個遞增的序列,進而避開了重複的部分(由於沒法選到小於index的座標的值)
        for(int i=index;i<candidates.length;i++){
            curr.add(candidates[i]);// 加入解集中
            myMethod(candidates,target,curr,sum+candidates[i],i);// 遞歸調用
            curr.remove(curr.size()-1);// 解集回溯
        }
    }

    public static void main(String[] args){
        CombinationSum cs = new CombinationSum();
        List<List<Integer>> lr = cs.combinationSum(new int[]{2,3,6,7},7);
        for(List<Integer> l:lr){
            System.out.println(l);
        }
    }

OK,就先記錄到這!

相關文章
相關標籤/搜索