22. Generate Parentheses

博客围绕LeetCode上括号生成问题的代码展开。给出题目链接及描述,分析了四段代码。指出代码一、二用String,因不可变重载+会生成新对象,恢不恢复现场结果相同;代码三、四用StringBuilder,因可变且Java传引用,必须恢复现场,还给出判断是否恢复现场的小技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

https://leetcode.com/problems/generate-parentheses/description/

描述

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

代码一

/**
该题只要保证在任意的位置:左括号的数量>=右括号的数量
满足上述条件就能使得是有效的组合
**/

class Solution {
    public List<String> generateParenthesis(int n) {
        if(n == 0) {
            return new ArrayList<>();
        }
        List<String> ans = new ArrayList<>();
        String str = new String();
        dfs(ans, str, 0, 0, n);
        return ans;
    }

    private void dfs(List<String> ans, String str, int leftCount, int rightCount, int n) {
        System.out.println(str);
        if(leftCount == n && rightCount == n) {
            ans.add(str);
            return;
        }
        int tempRight = rightCount;
        int tempLeft = leftCount;
        if(tempRight < tempLeft && tempLeft < n) {  // 如果当前右括号的数量少,并且左括号还能用,则当前既能选择左括号,又能选择右括号
            str = str + '(';
            leftCount++;
            dfs(ans, str,leftCount, rightCount, n);
            str = str.substring(0, str.length() - 1);
            leftCount--;
            str = str + ')';
            rightCount++;
            dfs(ans, str, leftCount, rightCount, n);
        }else if(tempRight == tempLeft) {  // 如果当前右括号的数量等于左括号的数量,则当前只能选择左括号
            str = str + '(';
            leftCount++;
            dfs(ans, str,leftCount, rightCount, n);
        } else if(tempLeft == n && tempRight < tempLeft) { // 如果当前右括号的数量少,并且左括号不能用,则当前只能选择右括号
            str = str + ')';
            rightCount++;
            dfs(ans, str, leftCount, rightCount, n);
        }

    }
}

代码二

class Solution {
    public List<String> generateParenthesis(int n) {
        if(n == 0) {
            return new ArrayList<>();
        }
        List<String> ans = new ArrayList<>();
        String str = new String();
        dfs(ans, str, 0, 0, n);
        return ans;
    }

    private void dfs(List<String> ans, String str, int leftCount, int rightCount, int n) {
        System.out.println(str);
        if(leftCount == n && rightCount == n) {
            ans.add(str);
            return;
        }
        int tempRight = rightCount;
        int tempLeft = leftCount;
         // 如果当前右括号的数量少,并且左括号还能用,则当前既能选择左括号,又能选择右括号
        if(tempRight < tempLeft && tempLeft < n) { 
            str = str + '(';
            leftCount++;
            dfs(ans, str,leftCount, rightCount, n);
            str = str.substring(0, str.length() - 1);
            leftCount--;
            str = str + ')';
            rightCount++;
            dfs(ans, str, leftCount, rightCount, n);
            str = str.substring(0, str.length() - 1);
            rightCount--;
        }else if(tempRight == tempLeft) {// 如果当前右括号的数量等于左括号的数量,则当前只能选择左括号
            str = str + '(';
            leftCount++;
            dfs(ans, str,leftCount, rightCount, n);
            str = str.substring(0, str.length() - 1);
            leftCount--;


        } else if(tempLeft == n && tempRight < tempLeft) {// 如果当前右括号的数量少,并且左括号不能用,则当前只能选择右括号
            str = str + ')';
            rightCount++;
            dfs(ans, str, leftCount, rightCount, n);
            str = str.substring(0, str.length() - 1);
            rightCount--;
        }

    }
}

代码三

/**
该题只要保证在任意的位置:左括号的数量>=右括号的数量
满足上述条件就能使得是有效的组合
**/

class Solution {
    public List<String> generateParenthesis(int n) {
        if(n == 0) {
            return new ArrayList<>();
        }
        List<String> ans = new ArrayList<>();
        StringBuilder str = new StringBuilder();
        dfs(ans, str, 0, 0, n);
        return ans;
    }

    private void dfs(List<String> ans, StringBuilder str, int leftCount, int rightCount, int n) {
        System.out.println(str);

        if(leftCount == n && rightCount == n) {
            ans.add(str.toString());
            return;
        }

        if(leftCount > n || rightCount >n) {
            return;
        }

        if(rightCount <= leftCount) { 
            str = str.append('(');
            dfs(ans, str,leftCount + 1, rightCount, n);
            str.setLength(str.length() - 1);
            str = str.append(')');
            dfs(ans, str, leftCount, rightCount + 1, n);
            str.setLength(str.length() - 1);
        }
    }
}

代码四

/**
该题只要保证在任意的位置:左括号的数量>=右括号的数量
满足上述条件就能使得是有效的组合
**/

class Solution {
    public List<String> generateParenthesis(int n) {
        if(n == 0) {
            return new ArrayList<>();
        }
        List<String> ans = new ArrayList<>();
        StringBuilder str = new StringBuilder();
        dfs(ans, str, 0, 0, n);
        return ans;
    }

    private void dfs(List<String> ans, StringBuilder str, int leftCount, int rightCount, int n) {
        System.out.println(str);

        if(leftCount == n && rightCount == n) {
            ans.add(str.toString());
            return;
        }

        if(leftCount > n || rightCount >n) {
            return;
        }

        if(rightCount <= leftCount) { 
            str = str.append('(');
            dfs(ans, str,leftCount + 1, rightCount, n);
            str.setLength(str.length() - 1);
            str = str.append(')');
            dfs(ans, str, leftCount, rightCount + 1, n);
        }
    }
}

疑问:代码一和代码二是一样的,只是差了在最后的时候,有没有重新设置为和原来的一样, 代码一和代码二都是正确的。但是,代码三和代码四也是相差在最后有没有设置为何原来一样的,但是代码三是正确的,代码四是错误的!!

解答:代码一和代码二用的是String,而string每次重载+操作符的时候,都会生成新的string,因为string是不可变的。而代码三和代码四用的是StringBuilder,每次都不会生成新的StringBuilder,因为它是可变的。由于Java是穿的引用,因此每次会修改原来的东西,但是因为String每次传递了一个新的东西,而且代码一和代码二的差别都是在最后返回的地方,因此恢不恢复现场都是一样的,而StringBuilder就不一样了,每次都是修改最开始的字符串,因此,必须修复现场。

小技巧,当不知道需不需要恢复现场时,只需要交换两次dfs的位置,如果先dfs哪个都行,则必须恢复现场!!

#include <cassert> /// for assert #include <iostream> /// for I/O operation #include <vector> /// for vector container /** @brief Backtracking algorithms @namespace backtracking / namespace backtracking { /* @brief generate_parentheses class */ class generate_parentheses { private: std::vectorstd::string res; ///< Contains all possible valid patterns void makeStrings(std::string str, int n, int closed, int open); public: std::vectorstd::string generate(int n); }; /** @brief function that adds parenthesis to the string. @param str string build during backtracking @param n number of pairs of parentheses @param closed number of closed parentheses @param open number of open parentheses */ void generate_parentheses::makeStrings(std::string str, int n, int closed, int open) { if (closed > open) // We can never have more closed than open return; if ((str.length() == 2 * n) && (closed != open)) { // closed and open must be the same return; } if (str.length() == 2 * n) { res.push_back(str); return; } makeStrings(str + ')', n, closed + 1, open); makeStrings(str + '(', n, closed, open + 1); } /** @brief wrapper interface @param n number of pairs of parentheses @return all well-formed pattern of parentheses */ std::vectorstd::string generate_parentheses::generate(int n) { backtracking::generate_parentheses::res.clear(); std::string str = “(”; generate_parentheses::makeStrings(str, n, 0, 1); return res; } } // namespace backtracking /** @brief Self-test implementations @returns void */ static void test() { int n = 0; std::vectorstd::string patterns; backtracking::generate_parentheses p; n = 1; patterns = {{“()”}}; assert(p.generate(n) == patterns); n = 3; patterns = {{“()()()”}, {“()(())”}, {“(())()”}, {“(()())”}, {“((()))”}}; assert(p.generate(n) == patterns); n = 4; patterns = {{“()()()()”}, {“()()(())”}, {“()(())()”}, {“()(()())”}, {“()((()))”}, {“(())()()”}, {“(())(())”}, {“(()())()”}, {“(()()())”}, {“(()(()))”}, {“((()))()”}, {“((())())”}, {“((()()))”}, {“(((())))”}}; assert(p.generate(n) == patterns); std::cout << “All tests passed\n”; } /** @brief Main function @returns 0 on exit */ int main() { test(); // run self-test implementations return 0; } 在这段代码的基础上为C++初学者出几个练习题?
最新发布
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值