leetcode
leetcode 2551 ~ 2600
括号

括号

难度:

标签:

题目描述

Implement an algorithm to print all valid (e.g., properly opened and closed) combinations of n pairs of parentheses.

Note: The result set should not contain duplicated subsets.

For example, given n = 3, the result should be:

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

代码结果

运行时间: 48 ms, 内存: 15.1 MB


/*
 * 问题思路:
 * 使用 Java Stream 的特性来生成所有合法的括号组合。
 * 使用递归的方法生成所有可能的括号序列。
 * 当字符串达到所需长度时,将其转换为流并收集到一个 List 中。
 */

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class GenerateParenthesesStream {

    public List<String> generateParenthesis(int n) {
        return generate("", 0, 0, n).collect(Collectors.toList());
    }

    private Stream<String> generate(String current, int open, int close, int max) {
        // 当达到最大长度时,返回当前序列
        if (current.length() == max * 2) {
            return Stream.of(current);
        }

        Stream<String> result = Stream.empty();

        // 递归生成所有可能的括号组合
        if (open < max) {
            result = Stream.concat(result, generate(current + "(", open + 1, close, max));
        }
        if (close < open) {
            result = Stream.concat(result, generate(current + ")", open, close + 1, max));
        }

        return result;
    }
}

解释

方法:

该题解使用回溯法来生成所有可能的合法括号组合。函数 `backtrack(path, stack)` 递归地构建括号串,其中 `path` 用于存储当前的括号组合,`stack` 用于模拟一个栈来确保括号的合法性(即,始终保持栈中的左括号数量不少于右括号)。递归终止条件是当 `path` 的长度达到 `2 * n` 且 `stack` 为空时,表示找到一个合法的组合。在递归过程中,基于当前 `stack` 的状态,选择添加 '(' 或 ')'。如果 `stack` 为空,只能添加 '(';如果 `stack` 的长度为 `n`(即已经添加了所有的左括号),只能添加 ')';否则,既可以添加 '(' 也可以添加 ')'。每次添加后,递归调用 `backtrack` 继续构建路径,之后撤销该步骤(回溯),以尝试其他可能性。

时间复杂度:

O(4^n / sqrt(n))

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在回溯过程中,选择添加'('后立即在递归调用后将其从stack中移除,这种处理方式的目的是什么?
在回溯算法中,添加'('后立即在递归调用后将其从stack中移除是为了恢复到递归前的状态,这使得算法能够探索其它所有可能的括号组合。具体到这个场景,'('被添加到stack代表着一个未匹配的左括号,一旦通过递归尝试了这个选择后,需要将其从stack中移除以撤销这一操作,从而保证每次递归返回时,stack的状态都与进入当前递归层时一致,确保算法正确性。
🦆
在实现中,如果stack的长度等于n,即已经添加了所有左括号,这里只能添加')'的逻辑是否充分考虑了所有可能的括号添加顺序?
是的,这个逻辑考虑了所有可能的括号添加顺序。在生成括号的问题中,任何时候添加的右括号数量都不能超过左括号数量。因此,如果stack的长度已经达到n,意味着所有n个左括号已经被添加到路径中,并且每个左括号都需要一个对应的右括号来匹配。此时,选择仅添加右括号')'是合理的,因为只有这样才能保证生成的括号字符串是有效的。
🦆
代码中的递归终止条件是path长度等于2n且stack为空,但是为什么还需要检查stack是否为空?
检查stack是否为空是为了确认所有添加的左括号都已被相应的右括号匹配。在生成括号的过程中,每个左括号'('都要求有一个对应的右括号')'来匹配,以确保括号字符串的有效性。如果在path长度达到2n时,stack不为空,意味着仍有未被匹配的左括号,这种情况下生成的括号组合是不完整且无效的。因此,终止条件同时包括path的长度和stack的状态是必要的。
🦆
递归函数backtrack中,path和stack分别代表了什么具体的逻辑角色和功能,这与传统的回溯算法框架有何异同?
在这个递归函数中,`path`代表当前构建中的括号字符串,是回溯算法中的'路径',用于记录到目前为止所做的选择。`stack`代表了未匹配的左括号的计数,是用来保证括号匹配的辅助工具,不同于传统回溯算法中只有路径的概念。传统回溯通常涉及选择列表和路径,而这里的stack相当于是对路径有效性的实时检查工具。这种设计使得每次添加或回撤操作都能保证路径的合法性,即确保任何时刻路径中的右括号数不超过左括号数。

相关问题