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() 回溯恢复现场。

相关推荐
residual_fan16 分钟前
船用柴油机动态机动工况故障诊断:控制感知仿射网络(CAAN)的技术实现
人工智能·算法·数据挖掘·数据分析
其实防守也摸鱼1 小时前
Cylab在线靶场——CTF中的通用技能
linux·数据库·学习·算法·靶场·ctf·学习网络安全
蓝悦无人机1 小时前
《Planning algorithms》读书笔记——第8章 反馈运动规划
算法·读书笔记·反馈运动规划
RaaS1001 小时前
Agent Skills 进了 Linux Foundation:2.8 亿次安装,但装 skill 还没有安全规范
人工智能·算法·架构
CV爱数码2 小时前
CT 重建从零开始(五):ASTRA Toolbox 上手——几何、投影器与算法
图像处理·算法·计算机视觉·ct
TunerT_TQ2 小时前
超立方体Ramsey(openai/math 家族171)H11攻坚日志|D60–D69:Star分组重数、二幂坐标群、奇偶入射语义与Site‑Level候选计数形式化闭环
算法
Rabitebla2 小时前
【系统性学习笔记】Linux 权限篇(二):文件属性、权限修改与目录权限的底层逻辑
linux·c++·笔记·学习·算法
远离UE42 小时前
半拉格朗日学习笔记 动力学 实时渲染
人工智能·算法
小郎君6582 小时前
MPTT 不是万能编号:一棵百万节点树的增删改与批量事务
算法
FPGA小徐2 小时前
【一生一芯 / PA】AM 核心文件逐行解析(上):从 start.S 到 gpu.c
前端·算法