括號生成-LeetCode22

Real_man發表於2019-04-21

想刷刷leetcode鍛鍊寫自己寫程式碼的能力,關於寫程式碼,我覺得它不僅僅是寫程式碼,而是寫你的想法。為什麼有的人會寫成一坨,而有的人程式碼工整,美感,這個一方面是一個人寫之前是否有思考清楚,另外一方面是寫程式碼的習慣了。

程式碼只是表達思想的一種

題目

給出 n 代表生成括號的對數,請你寫出一個函式,使其能夠生成所有可能的並且有效的括號組合。

例如,給出 n = 3,生成結果為:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]
複製程式碼

解題

首先想到的是,每一個位置都有兩種可能性:2^6 = 64。

  • 有一些很明顯就錯誤的,比如:"((( ((("
    • 排除顯而易見錯誤的,即左括號與右括號數量不等的
  • 有錯誤不是那麼明顯看出來的,比如:"))) (((" , "()) (()"
    • 開頭是右括號的,直接去掉,這個其實代表右括號已經出現的數量大於左括號出現的數量

正確的方案是,首先要放入左括號"(",後續要有一個與之對應的括號,同時在任何位置,有括號")"的數量不能大於"("的數量,否則,"())" 就是錯誤的。

程式碼

  1. 首先定義一個方法,要知道當前左括號與有括號的數量,並且知道當前的字串
    /**
     *
     * @param left 代表當前已經出現的左括號數量
     * @param right 代表當前已經出現的右括號數量
     * @param n 代表生成括號的對數
     * @param s 代表當前的字串,可能還未處理完
     * @param res 儲存所有括號字串的列表
     */
     private void generateParenthesis(int left, int right, int n, String s,List<String> res) {
         
     }
複製程式碼
  1. 目前比較喜歡的debug方式為通過列印,來看程式碼是否符合自己的預期,另外這次寫一下終止條件
    private void generateParenthesis(int left, int right, int n, String s,List<String> res) {

        System.out.printf("當前的字串為:%s ,左:%d,右%d \n",s,left,right);
        // 終止條件
        if (left == n && right == n){
            res.add(s);
            return;
        }
        ...
    }
複製程式碼
  1. 中間的執行過程。首先要放入左括號,然後再放入有括號
private void generateParenthesis(int left, int right, int n, String s,List<String> res) {

        System.out.printf("當前的字串為:%s ,左:%d,右%d \n",s,left,right);
        // 終止條件
        if (left == n && right == n){
            res.add(s);
            return;
        }

        // 保證首先新增的是左括號
        if (left < n){
            generateParenthesis(left+1,right,n,s + "(",res);
        }

        // 注意,這裡的right < left,代表的意思是
        // 首先我們肯定要放入左括號才是合法的,這樣的話,任何時刻,左括號的數量只能比右括號的數量多
        if (right < n && right < left){
            generateParenthesis(left,right+1,n,s + ")",res);
        }

    }
複製程式碼
  1. 最終程式碼為
public class Solution_22 {
    /**
     * n = 3 生成
     * [
     *   "((()))",
     *   "(()())",
     *   "(())()",
     *   "()(())",
     *   "()()()"
     * ]
     *
     * 每一個位置都有兩種可能性:2^6 = 64。
     *    有一些很明顯就錯誤的,比如:"((( ((("
     *        - 排除顯而易見錯誤的,即左括號與右括號數量不等的
     *    有錯誤不是那麼明顯看出來的,比如:"))) (((" , "()) (()"
     *        - 開頭是右括號的,直接去掉,這個其實代表右括號已經出現的數量大於左括號出現的數量
     *
     * 正確的方案是首先放左括號,後面只需要有對應的右括號才行
     */

    public static void main(String[] args) {
        List<String> list = new Solution_22().generateParenthesis(3);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

    public List<String> generateParenthesis(int n) {
        if(n == 0){
            return Collections.emptyList();
        }

        List res = new LinkedList<String>();
        generateParenthesis(0,0,n,"",res);
        return res;
    }

    /**
     *
     * @param left 代表當前已經出現的左括號數量
     * @param right 代表當前已經出現的右括號數量
     * @param n 代表生成括號的對數
     * @param s 代表當前的字串,可能還未處理完
     * @param res 儲存所有括號字串的列表
     */
    private void generateParenthesis(int left, int right, int n, String s,List<String> res) {

        System.out.printf("當前的字串為:%s ,左:%d,右%d \n",s,left,right);
        // 終止條件
        if (left == n && right == n){
            res.add(s);
            return;
        }

        // 保證首先新增的是左括號
        if (left < n){
            generateParenthesis(left+1,right,n,s + "(",res);
        }

        // 注意,這裡的right < left,代表的意思是
        // 首先我們肯定要放入左括號才是合法的,這樣的話,任何時刻,左括號的數量只能比右括號的數量多
        if (right < n && right < left){
            generateParenthesis(left,right+1,n,s + ")",res);
        }

    }
}
複製程式碼

執行效果

image-20190421094040759

最後

遞迴的程式碼有一個問題,一般不能執行太多的層數,否則會堆疊錯誤。

相關文章