leetcode22.括号生成

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

完整代码

当初的想法:
生成n个括号的排列组合,可以在生成n-1个括号的基础上进行处理,将括号分别加在其上一步实现的左面,右面,两边。
但是这会漏掉一些情况:当n=4时,“(())(())”这种情况会漏掉。

改进当初的想法:
生成n个括号的组合,依然是基于前面的结果进行处理,当初的想法只用到了第n-1步的值,导致结果会漏掉一些情况。这次不仅仅用到第n-1步的值,还会用到其他的结果,那就需要在求解的过程中将所有的结果保存。

其实本质思想是:动态规划

  • 【第n步的结果】=’(’+【第p步的结果】+’)’+【第q步的结果】(说明:p+q=n-1)
  • p:从0增加到n-1
  • q:从n-1减小到0
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<vector<string>> cell;
        vector<string> c;
        if(n<=0)
            return c;
        //为了便于处理,真正的存储结果从cell的第一行开始
        cell.push_back(c);//第0行保存空值
        c.push_back("()");
        cell.push_back(c);//第一行是n=1时的结果
        for(int i=2;i<=n;++i){           
            string temp="";
            c.clear();
            int p,q;            
            for(p=0;p<i;++p){
                if(p==0){                                        
                    q=i-1;
                    for(int k=0;k<cell[q].size();++k){//针对等于q时的每一个字符串进行处理
                        temp="()"+cell[q][k];
                        c.push_back(temp);//将当前情况下的结果保存
                    }
                }
                else{
                    for(int j=0;j<cell[p].size();++j){//针对等于p时的每一个字符串进行处理
                        temp='('+cell[p][j]+')';
                        if(p==i-1){//不再连接q;
                            c.push_back(temp);
                            continue;
                        }
                        q=i-1-p;
                        for(int k=0;k<cell[q].size();++k){//针对等于q时的每一个字符串进行处理
                            c.push_back(temp+cell[q][k]);//将当前情况下的结果保存
                        }
                    }
                }
                
            }
            cell.push_back(c);
        }
        return cell[n];
    }
};

比较精炼的写法
这里cell在n=0时,存入的是一个空串,大大方便了处理过程

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<vector<string>> cell;
        vector<string> c;
        if(n<=0)
            return c;
        //为了便于处理,真正的存储结果从cell的第一行开始
        // c.push_back("");
        // cell.push_back(c);//第0行保存空值
        // c.clear();//一定要将c中的内容清除后,再来存放新的值
        // c.push_back("()");
        // cell.push_back(c);//第一行是n=1时的结果
        /////////等价写法
        cell.push_back({""});//第0行
        cell.push_back({"()"});//第一行
        
        for(int i=2;i<=n;++i){           
            string temp="";
            c.clear();
            int p,q;            
            for(p=0;p<i;++p){   
                    cout<<"p="<<p<<" "<<cell[p].size()<<endl;
                    for(int j=0;j<cell[p].size();++j){//针对等于p时的每一个字符串进行处理
                        temp='('+cell[p][j]+')';
                        q=i-1-p;
                        cout<<"q="<<q<<" "<<cell[q].size()<<endl;
                        for(int k=0;k<cell[q].size();++k){//针对等于q时的每一个字符串进行处理
                            string str=temp+cell[q][k];                            
                            c.push_back(str);//将当前情况下的结果保存
                        }
                    }
                
                
            }
            cell.push_back(c);
        }
        return cell[n];
    }
};

特别注意:下面这种写法就不适合先存到一个vector的变量c中,再将c push_back到cell中,因为cell预先分配了空间。

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> c;
        if(n<=0)
            return c;
        vector<vector<string>> cell(n+1);        
        cell[0]={""};
        cell[1]={"()"};
        for(int i=2;i<=n;++i){           
            c.clear();                      
            for(int p=0;p<i;++p){
                for(string t1:cell[p]){//针对等于p时的每一个字符串进行处理 
                    int q=i-1-p;
                    for(string t2:cell[q]){//针对等于q时的每一个字符串进行处理
                        cell[i].push_back("("+t1+")"+t2);//将当前情况下的结果保存
                    }
                 }                
            }                     
        }        
        return cell[n];
    }
};

基本思想:回溯思想
参考:回溯

构建深度优先树:

  • 初始状态:有n个左括号,n个右括号
  • 产生左分支的条件:剩余左括号数>0时。只受自己剩余括号的数量的约束
  • 产生右分支的条件:剩余右括号数>0,且剩余的右括号数>=左括号数(等于也包含在内的原因是:初始情况能有分支产生,这也就引发了剪枝的出现,其实初始情况产生的分支也不会 满足条件,改成>,代码也一样通过)。同时会受左括号的约束
  • 剪枝的条件:剩余的右括号数<左括号数
  • 终止条件:剩余左右括号树都等于0时,该分支结束
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        //采用回溯的思想
        int left=n,right=n;
        vector<string> res;
        if(n<=0)
            return res;
        string c="";
        fun(left,right,res,c);
        return res;
    }
private:
    void fun(int left,int right,vector<string> &res,string &c){
        if(left==0&&right==0){//左右括号数剩余为0时,保存结果
            res.push_back(c);
            //cout<<c<<endl;
            return;
        }
        else if(right<left){//剩余的右分支数<左分支数,剪枝
            return;
        }
        
        if(left>0){//剩余左括号数>0,生成左分支
            c+='(';
            fun(left-1,right,res,c);
            c.pop_back();//重置为原来的状态
        }
        if(right>0&&right>=left){//剩余右括号数>0,且剩余右括号数>左括号数,生成右分支
            c+=')';
            fun(left,right-1,res,c);
            c.pop_back();//重置为原来的状态
        }
    }
};

说明:

  1. 上述剪枝的条件判断提高了代码的执行效率,如果没有剪枝,代码执行起来时间会用的更长。
  2. 上述回溯使用的是减法的形式,左右括号还剩几个可以用;同样,也可以使用加法的形式,左右括号使用了几个,代码逻辑完全一样,只不过中间判断的条件有变化。
发布了217 篇原创文章 · 获赞 9 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_31672701/article/details/103704911
今日推荐