NC26 题解 | #括号生成#

括号生成

http://www.nowcoder.com/practice/c9addb265cdf4cdd92c092c655d164ca

题意分析

  • 给你一个数字n,需要找到所有的合法的括号序列,括号的个数为n个。

  • 前置知识

    • 首先,我们需要知道什么是合法的括号序列?我们知道()这种括号序列是一个合法的序列,)(这种的括号序列不是一个合法的括号序列。那么如果A和B都是合法的括号序列,那么AB也是合法的括号序列,(AB)也是合法的括号序列。
    • 然后,我们需要了解什么是回溯法?回溯法就是在访问某一个子节点的时候,我们将状态改变了,但是为了能够正确地访问另一个子节点,我们需要将状态返回之前访问任何子节点时候的状态。在二叉树上面的体现如下:

图片说明
+ 最后,我们需要了解一下栈的基本原理和使用。栈是一种先进后出的数据结构。

图片说明

思路分析

思路一 全排列+栈验证

  • 既然,我们这个序列的所有的字符的数量都是固定的,n个左括号和n个右括号,最后的合法的排列一定是这个序列的一种排列的情况。那么我们就可以进行一下处理,我们先将2n个字符构造成一个初始的排列,((((...)))),构造完成之后,我们在利用C++的next_permutation库函数对这个字符串进行全排列,那么怎么验证呢?我们使用栈来模拟一个序列的放入过程。判断这个序列是否合法?

  • 代码如下

    • 我们进行了全排列,时间复杂度为n!,然后我们对每个排列进行验证,时间复杂度为n,那么总的时间复杂度为O(n!*n)
    • 每次我们对一个排列进行judge的时候,我们都需要利用一个栈进行处理,空间复杂度为O(n)
      class Solution {
      public:
      /**
      * 
      * @param n int整型 
      * @return string字符串vector
      */
      vector<char> v;
      // 利用栈来验证这个序列是否合法
      bool judge(vector<char> v){
        stack<char> st;
        for(auto x:v){
            // 如果此时栈为空,那么直接push
            if(st.empty()){
                st.push(x);
            }else{
                // 如果可以与栈顶抵消,那么栈顶出栈,否则push
                if(st.top()=='('&&x==')') st.pop();
                else st.push(x);
            }
        }
        return !st.size();
      }
      vector<string> generateParenthesis(int n) {
        // write code here
        // 先构造一个初始的有序的排列
        for(int i=0;i<n;i++){
            v.push_back('(');
        }
        for(int i=0;i<n;i++){
            v.push_back(')');
        }
        vector<string> ans;
        do{
            if(judge(v)){
                string tmp="";
                for(auto x:v){
                    tmp+=x;
                }
                ans.push_back(tmp);
            }
            // 利用permutation函数或得所有的序列组合
        }while(next_permutation(v.begin(),v.end()));
        return ans;
      }
      };

解法二 DFS回溯法

  • 回溯法的思想上面进行了介绍,其实这个写法就是上面的一种写法的优化,我们都知道,上面的写法不能再判断此时已知的序列不合法的情况下退出,而是继续进行排列,显然这是浪费时间的。而我们使用DFS回溯法就是在判断这个序列不合法的情况下进行及时退出,避免不必要的遍历。然后因为每个位置可能有两种不同的填法,所以需要进行回溯处理。同时,这里讲一下这个代码里面的sum,因为我们规定的是左括号为1,右括号为-1,那么假如sum<0的时候,此时的左括号一定小于右括号的,这一定是不合法的情况,所以可以直接返回。

  • 代码如下

    • 每个位置有两种不同的填法,时间复杂度为O(2^n)
    • 在进行dfs回溯的时候,由于我们的中间数组tmp是全局的,所以这部分空间复杂度为O(n),但是我们发现在每次递归的时候需要使用栈空间,每一层是O(1),最多n层, 所以是O(n),最后的空间复杂度为O(n)
      class Solution {
      public:
      /**
      * 
      * @param n int整型 
      * @return string字符串vector
      */
      vector<string> ans;
      vector<char> tmp;
      void dfs(int n,int l,int r,int sum){
        // 如果此时左括号或者右括号都大于n个,或者sum<0,说明此时不合法,直接返回
        if(l>n||r>n||sum<0){
            return;
        }
        // 合法则记录
        if(l==n&&r==n&&sum==0){
            string s;
            for(auto x:tmp){
                s+=x;
            }
            ans.push_back(s);
            return;
        }
        // 先递归将左括号放入的情况
        tmp.push_back('(');
        dfs(n,l+1,r,sum+1);
        // 回溯
        tmp.pop_back();
        // 执行右括号放入的情况
        tmp.push_back(')');
        dfs(n,l,r+1,sum-1);
        // 回溯
        tmp.pop_back();
      }
      vector<string> generateParenthesis(int n) {
        // write code here
        dfs(n,0,0,0);
        return ans;
      }
      };
全部评论

相关推荐

关于我大学本科四年,想了很多,但还是不知道该怎么动笔&nbsp;“大学四年,是我从懵懂少年走向职场青年的转折期。这一路跌跌撞撞,有迷茫,有遗憾,也有成长和决心。”&nbsp;大一刚进来时仍然有高中那股学习劲,经常一个人去图书馆学高等数学,但后面劲头一过便开始在宿舍开启躺平生活(现在想想那段时间真的很爽,无忧无虑)。由于大一担任班干部,所以经常要跟其他班的班干部交流,在此期间认识了隔壁班的一位女生,短发而很可爱,因为很多团建还有比赛都是我们两班一起参加的,而且我和她都是负责人,所以交集很多,后面慢慢地彼此对产生了好感,所以在大一刚开学的2个月后,我们在一起了,彼此之前都是初恋。但当时我真的是太太太直男了,对感情的想...
真烦好烦真烦:骗哥们可以,别把你自己也骗到了就行。哥们被你骗了真无所谓的,打个哈哈就过了。但希望你打完这段话后擦一下眼角,别让眼泪掉在手机屏幕上了就行。你说的这些话,哥们信一下也是没什么的。还能让你有个心里安慰,但这种话说出来骗骗兄弟就差不多得了,哥们信你一下也不会少块肉,但是你别搞得自己也当真了就行。哥们被你骗一下是真无所谓的,兄弟笑笑也就过去了。真不是哥们想要破你防,你擦擦眼泪好好想想,除了兄弟谁还会信你这些话?
点赞 评论 收藏
分享
嵌入式小辣鸡:包装好一点,校内的奖项可以不用写,校内项目经历最后两点写的太差了,详细讲一下内容,名字变一下。只需要写项目实现了什么,自己在其中做了什么就好,查看图片
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务