首页 > 其他 > 详细

leetcode题解之括号生成

时间:2020-06-11 14:11:25      阅读:49      评论:0      收藏:0      [点我收藏+]

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

 

示例:

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

??视频题解

code:

vid:

uuid:

requestId:

播放时间:

提示信息

技术分享图片
字幕
    倍速
      清晰度
        音轨
          倍速正常
          字幕Off
          音轨
          清晰度

          ??文字题解

          方法一:暴力法

          思路

          我们可以生成所有 22n2^{2n}‘(‘‘)‘ 字符构成的序列,然后我们检查每一个是否有效即可。

          算法

          为了生成所有序列,我们可以使用递归。长度为 n 的序列就是在长度为 n-1 的序列前加一个 ‘(‘‘)‘

          为了检查序列是否有效,我们遍历这个序列,并使用一个变量 balance 表示左括号的数量减去右括号的数量。如果在遍历过程中 balance 的值小于零,或者结束时 balance 的值不为零,那么该序列就是无效的,否则它是有效的。

          class Solution {
              public List<String> generateParenthesis(int n) {
                  List<String> combinations = new ArrayList();
                  generateAll(new char[2 * n], 0, combinations);
                  return combinations;
              }
          
          <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">generateAll</span><span class="hljs-params">(<span class="hljs-keyword">char</span>[] current, <span class="hljs-keyword">int</span> pos, List&lt;String&gt; result)</span> </span>{
              <span class="hljs-keyword">if</span> (pos == current.length) {
                  <span class="hljs-keyword">if</span> (valid(current))
                      result.add(<span class="hljs-keyword">new</span> String(current));
              } <span class="hljs-keyword">else</span> {
                  current[pos] = <span class="hljs-string">‘(‘</span>;
                  generateAll(current, pos+<span class="hljs-number">1</span>, result);
                  current[pos] = <span class="hljs-string">‘)‘</span>;
                  generateAll(current, pos+<span class="hljs-number">1</span>, result);
              }
          }
          
          <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">valid</span><span class="hljs-params">(<span class="hljs-keyword">char</span>[] current)</span> </span>{
              <span class="hljs-keyword">int</span> balance = <span class="hljs-number">0</span>;
              <span class="hljs-keyword">for</span> (<span class="hljs-keyword">char</span> c: current) {
                  <span class="hljs-keyword">if</span> (c == <span class="hljs-string">‘(‘</span>) balance++;
                  <span class="hljs-keyword">else</span> balance--;
                  <span class="hljs-keyword">if</span> (balance &lt; <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
              }
              <span class="hljs-keyword">return</span> (balance == <span class="hljs-number">0</span>);
          }
          

          }


          class Solution:
          def generateParenthesis(self, n: int) -> List[str]:
          def generate(A):
          if len(A) == 2*n:
          if valid(A):
          ans.append("".join(A))
          else:
          A.append(‘(‘)
          generate(A)
          A.pop()
          A.append(‘)‘)
          generate(A)
          A.pop()

              <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">valid</span><span class="hljs-params">(A)</span>:</span>
                  bal = <span class="hljs-number">0</span>
                  <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> A:
                      <span class="hljs-keyword">if</span> c == <span class="hljs-string">‘(‘</span>: bal += <span class="hljs-number">1</span>
                      <span class="hljs-keyword">else</span>: bal -= <span class="hljs-number">1</span>
                      <span class="hljs-keyword">if</span> bal &lt; <span class="hljs-number">0</span>: <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
                  <span class="hljs-keyword">return</span> bal == <span class="hljs-number">0</span>
          
              ans = []
              generate([])
              <span class="hljs-keyword">return</span> ans
          


          class Solution {
          bool valid(const string& str) {
          int balance = 0;
          for (char c : str)
          if (c == ‘(‘)
          ++balance;
          else {
          --balance;
          if (balance < 0)
          return false;
          }
          return balance == 0;
          }

          <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">generate_all</span><span class="hljs-params">(<span class="hljs-built_in">string</span>&amp; current, <span class="hljs-keyword">int</span> n, <span class="hljs-built_in">vector</span>&lt;<span class="hljs-built_in">string</span>&gt;&amp; result)</span> </span>{
              <span class="hljs-keyword">if</span> (n == current.size()) {
                  <span class="hljs-keyword">if</span> (valid(current))
                      result.push_back(current);
                  <span class="hljs-keyword">return</span>;
              }
              current += <span class="hljs-string">‘(‘</span>;
              generate_all(current, n, result);
              current.pop_back();
              current += <span class="hljs-string">‘)‘</span>;
              generate_all(current, n, result);
              current.pop_back();
          }
          

          public:
          vector<string> generateParenthesis(int n) {
          vector<string> result;
          string current;
          generate_all(current, n * 2, result);
          return result;
          }
          };

          复杂度分析

          • 时间复杂度:O(22nn)O(2^{2n}n),对于 22n2^{2n} 个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)O(n)

          • 空间复杂度:O(n)O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1)O(1) 的空间,最多递归 2n2n 层,因此空间复杂度为 O(n)O(n)

          方法二:回溯法

          思路和算法

          方法一还有改进的余地:我们可以只在序列仍然保持有效时才添加 ‘(‘ or ‘)‘,而不是像 方法一 那样每次添加。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点,

          如果左括号数量不大于 nn,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

          class Solution {
              public List<String> generateParenthesis(int n) {
                  List<String> ans = new ArrayList();
                  backtrack(ans, new StringBuilder(), 0, 0, n);
                  return ans;
              }
          
          <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">backtrack</span><span class="hljs-params">(List&lt;String&gt; ans, StringBuilder cur, <span class="hljs-keyword">int</span> open, <span class="hljs-keyword">int</span> close, <span class="hljs-keyword">int</span> max)</span></span>{
              <span class="hljs-keyword">if</span> (cur.length() == max * <span class="hljs-number">2</span>) {
                  ans.add(cur.toString());
                  <span class="hljs-keyword">return</span>;
              }
          
              <span class="hljs-keyword">if</span> (open &lt; max) {
                  cur.append(<span class="hljs-string">‘(‘</span>);
                  backtrack(ans, cur, open+<span class="hljs-number">1</span>, close, max);
                  cur.deleteCharAt(cur.length() - <span class="hljs-number">1</span>);
              }
              <span class="hljs-keyword">if</span> (close &lt; open) {
                  cur.append(<span class="hljs-string">‘)‘</span>);
                  backtrack(ans, cur, open, close+<span class="hljs-number">1</span>, max);
                  cur.deleteCharAt(cur.length() - <span class="hljs-number">1</span>);
              }
          }
          

          }


          class Solution:
          def generateParenthesis(self, n: int) -> List[str]:
          ans = []
          def backtrack(S, left, right):
          if len(S) == 2 * n:
          ans.append(‘‘.join(S))
          return
          if left < n:
          S.append(‘(‘)
          backtrack(S, left+1, right)
          S.pop()
          if right < left:
          S.append(‘)‘)
          backtrack(S, left, right+1)
          S.pop()

              backtrack([], <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
              <span class="hljs-keyword">return</span> ans
          


          class Solution {
          void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
          if (cur.size() == n * 2) {
          ans.push_back(cur);
          return;
          }
          if (open < n) {
          cur.push_back(‘(‘);
          backtrack(ans, cur, open + 1, close, n);
          cur.pop_back();
          }
          if (close < open) {
          cur.push_back(‘)‘);
          backtrack(ans, cur, open, close + 1, n);
          cur.pop_back();
          }
          }
          public:
          vector<string> generateParenthesis(int n) {
          vector<string> result;
          string current;
          backtrack(result, current, 0, 0, n);
          return result;
          }
          };

          复杂度分析

          我们的复杂度分析依赖于理解 generateParenthesis(n) 中有多少个元素。这个分析超出了本文的范畴,但事实证明这是第 nn 个卡特兰数 1n+1(2nn)\dfrac{1}{n+1}\dbinom{2n}{n},这是由 4nnn\dfrac{4^n}{n\sqrt{n}} 渐近界定的。

          • 时间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}}),在回溯过程中,每个答案需要 O(n)O(n) 的时间复制到答案数组中。

          • 空间复杂度:O(n)O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1)O(1) 的空间,最多递归 2n2n 层,因此空间复杂度为 O(n)O(n)

          方法三:按括号序列的长度递归

          思路与算法

          任何一个括号序列都一定是由 ( 开头,并且第一个 ( 一定有一个唯一与之对应的 )。这样一来,每一个括号序列可以用 (a)b 来表示,其中 ab 分别是一个合法的括号序列(可以为空)。

          那么,要生成所有长度为 2 * n 的括号序列,我们定义一个函数 generate(n) 来返回所有可能的括号序列。那么在函数 generate(n) 的过程中:

          • 我们需要枚举与第一个 ( 对应的 ) 的位置 2 * i + 1
          • 递归调用 generate(i) 即可计算 a 的所有可能性;
          • 递归调用 generate(n - i - 1) 即可计算 b 的所有可能性;
          • 遍历 ab 的所有可能性并拼接,即可得到所有长度为 2 * n 的括号序列。

          为了节省计算时间,我们在每次 generate(i) 函数返回之前,把返回值存储起来,下次再调用 generate(i) 时可以直接返回,不需要再递归计算。

          class Solution {
              ArrayList[] cache = new ArrayList[100];
              public List<String> generate(int n) {
                  if (cache[n] != null) {
                      return cache[n];
                  }
                  ArrayList<String> ans = new ArrayList();
                  if (n == 0) {
                      ans.add("");
                  } else {
                      for (int c = 0; c < n; ++c)
                          for (String left: generate(c))
                              for (String right: generate(n - 1 - c))
                                  ans.add("(" + left + ")" + right);
                  }
                  cache[n] = ans;
                  return ans;
              }
              public List<String> generateParenthesis(int n) {
                  return generate(n);
              }
          }
          
          class Solution:
              @lru_cache(None)
              def generateParenthesis(self, n: int) -> List[str]:
                  if n == 0:
                      return [‘‘]
                  ans = []
                  for c in range(n):
                      for left in self.generateParenthesis(c):
                          for right in self.generateParenthesis(n-1-c):
                              ans.append(‘({}){}‘.format(left, right))
                  return ans
          
          class Solution {
              shared_ptr<vector<string>> cache[100] = {nullptr};
          public:
              shared_ptr<vector<string>> generate(int n) {
                  if (cache[n] != nullptr)
                      return cache[n];
                  if (n == 0) {
                      cache[0] = shared_ptr<vector<string>>(new vector<string>{""});
                  } else {
                      auto result = shared_ptr<vector<string>>(new vector<string>);
                      for (int i = 0; i != n; ++i) {
                          auto lefts = generate(i);
                          auto rights = generate(n - i - 1);
                          for (const string& left : *lefts)
                              for (const string& right : *rights)
                                  result -> push_back("(" + left + ")" + right);
                      }
                      cache[n] = result;
                  }
                  return cache[n];
              }
              vector<string> generateParenthesis(int n) {
                  return *generate(n);
              }
          };
          

          复杂度分析

          • 时间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}}),该分析与 方法二 类似。

          • 空间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}}),此方法除答案数组外,中间过程中会存储与答案数组同样数量级的临时数组,是我们所需要的空间复杂度。

          leetcode题解之括号生成

          原文:https://www.cnblogs.com/leetcodetijie/p/13092317.html

          (0)
          (0)
             
          举报
          评论 一句话评论(0
          分享档案
          最新文章
          教程昨日排行
          关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
          © 2014 bubuko.com 版权所有
          打开技术之扣,分享程序人生!