• 22.括号生成


    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。

    示例:

    输入:n = 3
    输出:[
           "((()))",
           "(()())",
           "(())()",
           "()(())",
           "()()()"
         ]
    

    解法一

    又臭又长,还超时。

     vector<string> ans;
        string brackets;
        map<string,int> m;
        bool isValid(string s){
            stack<char> st;
            if(s[0]==')') return false;
            st.push('(');
            for(int i=1;i<s.size();++i){
                //访问栈顶元素要先检查栈不空,之前没有检查导致好久才看出错误。
                if(s[i]==')'&&st.size()!=0&&st.top()=='(') {
                    st.pop();
                }
                else if(s[i]==')') return false;
                else st.push('(');
            }
            return true;
        }
        void backtrack(int t,int n,string cur){
            if(t>=2*n) {
                if(m[cur]==0)
                    ans.push_back(cur);
                m[cur]++;
                return;
            }
            for(int i=t;i<2*n;++i){
                swap(cur[i],cur[t]);
                if(!isValid(cur.substr(0,t+1))) return;
                else{
                backtrack(t+1,n,cur);
                swap(cur[i],cur[t]);
                }
            }
        }
        vector<string> generateParenthesis(int n) {
            for(int i=0;i<n;++i) brackets+="()";
            backtrack(0,n,brackets);
            return ans;
        }
    

    花了好久才看出越界访问的原因,原来是访问栈顶元素前没有检查它是否为空:

    分析:

    这里是用排列树的思想,但实际上只有左括号和右括号两种符号,因此不仅时间复杂度大而且会产生重复结果。
    

    解法二

    使用双递归的方法:

     vector<string> ans;
        void backtrack(int left,int right,string cur){
            if(left==0&&right==0) {
                ans.push_back(cur);
                return;
            }
            //替换下面两行顺序,即先遍历右子树再遍历左子树也是可以的。
            if(left>0) backtrack(left-1,right,cur+'(');
            //只有当剩余')'比'('多时才能添加')'。
            if(right>left) backtrack(left,right-1,cur+')');
        }
        vector<string> generateParenthesis(int n) {
            backtrack(n,n,"");
            return ans;
        }
    

    tips:

    只有在剩余的右括号不少于左括号时才有可能符合要求。
    **遇到这种每次只有两种选择的问题可以使用双递归(二叉树遍历)方法。**
    因为用二叉树的思想,不同路径对应不同结果,因此不会有重复结果。
    

    分析:

    只有左括号和右括号两种选择,可以考虑双递归也即用二叉树的思想。这种算法实际上就是二叉树的先序遍历。当还剩左括号时遍历左子树(加左括号);当剩余右括号多于左括号时遍历右子树(加右括号)。要注意的是只有在剩余右括号多于左括号时才能加右括号,否则得到的结果必然不符要求。
    

    解法三

    也是使用双递归的方法,是对解法一的改进。

     vector<string> ans;
        bool isValid(const string& s){
            stack<char> st;
            if(s[0]==')') return false;
            st.push('(');
            for(int i=1;i<s.size();++i){
                //访问栈顶元素要先检查栈不空,之前没有检查导致好久才看出错误。
                if(s[i]==')'&&st.size()!=0&&st.top()=='(') {
                    st.pop();
                }
                else if(s[i]==')') return false;
                else st.push('(');
            }
            return true;
        }
        void backtrack(int left,int right,string cur){
            if(left==0&&right==0) {
                if(isValid(cur))
                    ans.push_back(cur);
                return;
            }
            //没有检查当前生成字符串的合法性,而是把检查过程放到了最后。
            if(left>0) backtrack(left-1,right,cur+'(');
            if(right>0) backtrack(left,right-1,cur+')');
        }
        vector<string> generateParenthesis(int n) {
            backtrack(n,n,"");
            return ans;
        }
    
  • 相关阅读:
    ubuntu 12.04 配置iscsi共享及挂载iscsi共享
    python_数据类型
    python_基本操作
    shell习题第5题:批量更改文件后缀名
    shell习题第4题:监控ip地址存活
    shell习题第3题:统计内存大小
    shell习题第2题:统计ip访问量
    shell习题第1题:每日一文件
    IIS网站的应用程序与虚拟目录的区别及应用
    http状态码
  • 原文地址:https://www.cnblogs.com/Frank-Hong/p/13302333.html
Copyright © 2020-2023  润新知