leetcode 22括号生成

复制代码
class Solution {
public:
    vector<string> ans; // 保存所有合法的括号组合
    string t;           // 当前正在构造的括号字符串

    void dfs(int l, int r, int n) {
        // 左右括号都已经使用 n 个,得到一个合法答案
        if (l == n && r == n) {
            ans.push_back(t);
            return;
        }

        // 1. 放左括号:左括号还没有使用完
        if (l < n) {
            t.push_back('(');   // 做选择
            dfs(l + 1, r, n);   // 左括号使用数量 +1
            t.pop_back();       // 回溯,撤销选择
        }

        // 2. 放右括号:右括号数量必须少于左括号数量
        if (r < l) {
            t.push_back(')');   // 做选择
            dfs(l, r + 1, n);   // 右括号使用数量 +1
            t.pop_back();       // 回溯,撤销选择
        }
    }

    vector<string> generateParenthesis(int n) {
        // 初始时左右括号都还没有使用
        dfs(0, 0, n);

        return ans;
    }
};

1. ans

复制代码
vector<string> ans;

用于保存最终所有合法括号组合。

例如:

复制代码
n = 3

最后:

复制代码
ans = [
    "((()))",
    "(()())",
    "(())()",
    "()(())",
    "()()()"
]

2. t

复制代码
string t;

表示:

当前 DFS 路径正在构造的括号字符串。

例如搜索过程中可能依次变成:

复制代码
""
"("
"(("
"(()"
"(())"

每次:

复制代码
t.push_back('(');

或者:

复制代码
t.push_back(')');

都是在当前路径上添加一个括号。

而:

复制代码
t.pop_back();

就是回溯,删除刚刚加入的括号。


3. l

复制代码
int l

表示:

已经使用了多少个左括号 (

例如:

复制代码
t = "(()"

里面有两个左括号:

复制代码
l = 2

注意这里的 l 不是"还剩多少个左括号",而是:

已经用了多少个。


4. r

复制代码
int r

表示:

已经使用了多少个右括号 )

比如:

复制代码
t = "(()"

里面有一个右括号:

复制代码
r = 1

所以此时:

复制代码
l = 2
r = 1

5. n

复制代码
int n

表示总共需要使用:

复制代码
n 个左括号
n 个右括号

例如:

复制代码
n = 3

最终字符串长度一定是:

复制代码
2 × n = 6

dfs() 函数是干什么的

复制代码
void dfs(int l, int r, int n)

可以直接理解成:

当前已经使用了 l 个左括号和 r 个右括号,继续往后构造合法的括号字符串。

所以:

复制代码
dfs(0, 0, n);

表示:

现在一个括号都没放,从头开始搜索。


第一个终止条件

复制代码
if (l == n && r == n) {
    ans.push_back(t);
    return;
}

如果:

复制代码
l == n
r == n

说明:

复制代码
n 个左括号全部用了
n 个右括号全部用了

那么当前字符串已经构造完成。

例如:

复制代码
n = 3

t = "(()())"

l = 3
r = 3

所以把:

复制代码
ans.push_back(t);

保存下来。


为什么 l < n 才能放左括号

复制代码
if (l < n)

因为总共只能使用 n 个左括号。

比如:

复制代码
n = 3
l = 2

说明目前用了两个:

复制代码
((

还可以再放一个。

但是如果:

复制代码
l = 3

已经用满了,就不能继续:

复制代码
t.push_back('(');

否则会超过题目要求。

所以:

复制代码
if (l < n)

意思就是:

左括号还有剩余,可以继续放。


为什么放左括号以后是 l + 1

复制代码
t.push_back('(');

dfs(l + 1, r, n);

因为刚才使用了一个左括号。

例如原来:

复制代码
l = 1
r = 0
t = "("

再加入:

复制代码
'('

变成:

复制代码
t = "(("
l = 2
r = 0

所以递归:

复制代码
dfs(l + 1, r, n);

为什么右括号必须满足 r < l

这是这道题最重要的条件:

复制代码
if (r < l)

表示:

只有当前已经使用的右括号数量比左括号少,才能继续放右括号。

例如:

复制代码
t = "(("
l = 2
r = 0

可以放:

复制代码
)

得到:

复制代码
"(()"

没有问题。

但是如果当前:

复制代码
t = "()"
l = 1
r = 1

此时如果继续放右括号:

复制代码
"())"

就非法了。

所以当:

复制代码
r == l

的时候不能再放右括号。

必须:

复制代码
r < l

才能放。


整个 DFS 在做什么

每一层其实就是决定:

复制代码
下一个位置放什么?

有两个可能:

复制代码
            当前状态
           /       \
        放左括号    放右括号
        l < n      r < l

所以代码就是:

复制代码
// 可以放左括号
if (l < n) {
    t.push_back('(');
    dfs(l + 1, r, n);
    t.pop_back();
}

// 可以放右括号
if (r < l) {
    t.push_back(')');
    dfs(l, r + 1, n);
    t.pop_back();
}

最后记住这几个变量

复制代码
ans → 保存最终答案

t   → 当前正在构造的字符串

l   → 已经用了多少个左括号

r   → 已经用了多少个右括号

n   → 左右括号分别需要使用多少个

整道题一句话总结:

DFS 每一层决定当前位置放左括号还是右括号;左括号只要 l < n 就可以放,右括号只有 r < l 时才能放,从而保证括号始终合法;递归结束后用 pop_back() 回溯恢复现场。

相关推荐
Navigator_Z1 小时前
LeetCode //C - 1237. Find Positive Integer Solution for a Given Equation
c语言·算法·leetcode
政企项目老覃12 小时前
大模型幻觉治理与自动评测:金融风控场景的落地实践
人工智能·算法·机器学习
淡海水13 小时前
08-03-不可变-ImmutableDictionary-TKey-TValue-与ImmutableHashSet-T-持久化哈希树
数据结构·算法·c#·哈希算法·dictionary·immutable
hansang_IR13 小时前
【题解】[APIO2023] 赛博乐园 / cyberland
c++·算法·图论
洛阳纸贵13 小时前
MATLAB-matlab基础知识
学习·算法·matlab
落羽的落羽13 小时前
【AI】快速理解AI应用的相关名词概念
linux·c++·人工智能·python·计算机网络·算法
Nil20813 小时前
leetcode 17电话号码的字母组合
算法·leetcode·职场和发展
203号居民15 小时前
LeetCode hot 100 — 25. K 个一组翻转链表
算法·leetcode·链表