Leetcode22——括號生成(回溯法)

1. 寫在前面

回溯[sù]:意思是逆着水流的方向走、逆水而行,逆流而上1。我一直讀成[shuò]。
框[kuàng]架

1.建築工程中,由樑、柱等聯結而成的結構:完成主體~工程。
2.比喻事物的基本組織、結構:這部長篇小說已經有了一個大致的~。

 得,語文沒學好。

2. 回溯法基本思想

 在許多遞歸問題當中,我們採取的方法都是窮盡所有的可能,從而找出合法的解。但是在某些情況下,當遞歸到某一層的時候,根據設置的判斷條件,可以 judge 此解是不合法的。在這種情況下,我們就沒必要再進行深層次的遞歸,從而可以提高算法效率。這一類算法我們稱爲“回溯法”,設置的判斷條件稱爲“剪枝函數”。2
 個人覺得啊,回溯法可以類比爲一棵自上而下的樹,每一次滿足題目要求的選擇就是一個節點。當滿足題目結束條件時,根到當前葉子節點的路徑就是一個可行解。

3. 回溯法基本框架

 回溯法框架3

1、路徑:也就是已經做出的選擇。
2、選擇列表:也就是你當前可以做的選擇。
3、結束條件:也就是到達決策樹底層,無法再做選擇的條件。
代碼方面,回溯算法的框架:
result = []
def backtrack(路徑, 選擇列表):
    if 滿足結束條件:
        result.add(路徑)
        return

for 選擇 in 選擇列表:
    做選擇
    backtrack(路徑, 選擇列表)
    撤銷選擇

其核心就是 for 循環裏面的遞歸,在遞歸調用之前「做選擇」,在遞歸調用之後「撤銷選擇」,特別簡單。

4. 題目描述

  1. 括號生成
    數字 n 代表生成括號的對數,請你設計一個函數,用於能夠生成所有可能的並且 有效的 括號組合。
    示例:
    輸入:n = 3
    輸出:[
    “((()))”,
    “(()())”,
    “(())()”,
    “()(())”,
    “()()()”
    ]

5. 代碼

 參考回溯法框架三點的解題。

  • 1.路徑:也就是已經做出的選擇。 ->這裏使用StringBuilder保存已走路徑,方便到時候回溯 — stringBuilder.deleteCharAt(X);
  • 2.選擇列表:也就是你當前可以做的選擇。 ->每次都可以選擇 左括號 或者 右括號,但有限制條件:1.左右括號數量均不能超過n。2.當前StringBuilder的右括號數量必須小於左括號數量
  • 3.結束條件:也就是到達決策樹底層,無法再做選擇的條件。->StringBuilder長度爲2*n。
	List<String> result = new ArrayList<String>();

    public List<String> generateParenthesis(int n) {
        //1、路徑:也就是已經做出的選擇。
        StringBuilder stringBuilder = new StringBuilder();

        //本題有兩個需要注意:
        // 1.左括號和右括號數目均不能超過n
        // 2.如何判斷括號合法?————————————————————當前已保存的括號數關係需滿足:right括號數 <= left括號數。


        backTrack(n, 0, 0, stringBuilder);
        return result;
    }

 	/**
     * 回溯計算
     *
     * @param num           括號對數
     * @param leftNum       已有左括號數量
     * @param rightNum      已有右括號數量
     * @param stringBuilder 已保存的路徑
     */
    private void backTrack(int num, int leftNum, int rightNum, StringBuilder stringBuilder) {

        //結束條件
        if (stringBuilder.length() >= num * 2) {
            result.add(stringBuilder.toString());
            return;
        }

        //做選擇
        if (leftNum < num) {
            stringBuilder.append("(");
            backTrack(num, ++leftNum, rightNum, stringBuilder);
            //撤銷選擇
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            --leftNum;
        }

        //咱再做一次選擇
        if (rightNum < leftNum) {
            stringBuilder.append(")");
            backTrack(num, leftNum, ++rightNum, stringBuilder);
            //撤銷選擇
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            --rightNum;
        }
    }

6. 總結

 一開始做題用的是暴力遞歸法,當括號總數達到2*n時才判斷括號是否有效"((((((",看了題解之後才知道回溯法。回溯法題目做的還是不多,得多刷幾題找找感覺。加油!

 	/**
     * 判斷括號是否有效
     * @param array char數組
     * @return 是否有效
     */
    private boolean parenthesIsValid(char[] array) {
        int balance = 0;

        for (char c : array) {
            if ('(' == c) {
                balance++;
            } else {
                balance--;
            }

            if (balance < 0) {
                return false;
            }
        }

        return balance == 0;

    }

完~


  1. 百度百科. ↩︎

  2. [算法筆記] 回溯法總結. ↩︎

  3. Leetcode刷題java之39. 組合總和(回溯法以及回溯法框架). ↩︎

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