力扣22 括号生成(C++)

题目


知识点

function定义的递归函数

CSDN详细解释链接

B站视频讲解【C++ function使用说明】

https://player.bilibili.com/player.html?bvid=BV1XaaEe1Epz&autoplay=0

emplace_back()

知乎详细解释链接

在 vector 容器的尾部添加一个元素。


思路------K神

选与不选

三种情况:

  1. 右括号的个数等于最大值
  2. 左括号的个数小于最大值
  3. 右括号的个数小于左括号的个数

本质是「选或不选」的思想,你可以把填左括号视作「选」,填右括号视作「不选」。

递归的过程中,要保证右括号的个数不能超过左括号的个数。

如果现在右括号个数等于左括号个数,那么不能填右括号。

如果现在右括号个数小于左括号个数,那么可以填右括号。

由于左括号个数始终 ≥ 右括号个数,且至多填 n 个左括号,所以当我们填了 n 个右括号时,也一定填了 n 个左括号,此时填完所有 2n 个括号。

答疑

问:什么时候需要写恢复现场,什么时候不需要写?

答:下面代码中,如果初始化 path 为空列表,就需要写恢复现场。本题由于所有括号长度都是固定的 2n,我们可以创建一个长为 2n 的 path 列表,在递归时直接写入字符(而不是插入字符),这样做无需写恢复现场。

枚举(难理解 但必须掌握)

用「枚举选哪个」的思路。在从左往右填的过程中,要时刻保证右括号的个数不能超过左括号的个数。

如果前面填了 5 个左括号,2 个右括号,那么还能填几个右括号?至多填 5−2=3 个。

所以枚举(在填下一个左括号之前)填入了 0,1,2,3 个右括号,这样就能得到下一个左括号的位置。

为了方便,代码直接用 balance 表示左右括号之差。这样我们枚举的范围就是 0 , balance。

注意最后一个左括号的右边还可以填右括号,但无需考虑。填入所有左括号后,剩余的位置我们会自动填入右括号。

不直接构建括号字符串,而是记录所有左括号的位置。


错误

function()函数格式

function<void(int,int)> dfs = \&(int left,int right) {};

right<=balance

right <= balance 中的等于号允许我们在放置下一个左括号之前,匹配掉所有当前未匹配的左括号。这是生成像 ()() 这样括号对完全独立(非嵌套)的组合所必需的。

如果不允许等于号(即 right < balance),那么每次放下一个左括号前必须至少保留一个未匹配的左括号,这样就会强制所有括号对必须嵌套,无法生成并列的括号对。


题解

选与不选

cpp 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> ans;//存放最终结果 括号组成的所有形式
        string path(n * 2, 0);//长度为2n的字符串,初始值都为0

        //使用function定义的递归函数,使用std::function
        //void(int, int)函数接受两个int参数,返回值为void
        function<void(int, int)> dfs = [&](int left, int right) {//以引用的方式获取所有外部变量:ans/path/n
            //当右括号数量达到n时,说明已经填满了2n个位置
            if (right == n) {
                ans.emplace_back(path);//在 vector 容器的尾部添加一个元素
                return;
            }

            //左括号还可以添加(还没达到n个)
            if (left < n) {
                path[left + right] = '(';//left+right 是已经填写的括号总数,也就是下一个要填的位置
                dfs(left + 1, right);//左括号数量+1
            }

            //右括号数量小于左括号数量
            if (right < left) {
                path[left + right] = ')';
                dfs(left, right + 1);
            }
        };

        dfs(0, 0);
        return ans;
    }
};

枚举

cpp 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> ans;
        vector<int> path;//记录左括号们的下标

        //i当前要填写的位置下标,balance = 已填的左括号数 - 已填的右括号数
        function<void(int, int)> dfs = [&](int i, int balance) {
            if (path.size() == n) {
                string s(n * 2, ')');//创建一个长度为2n,元素全是右括号的字符串s
                for (int j : path) {
                    s[j] = '(';
                }//把下标为path的位置都改成左括号
                ans.emplace_back(s);//存入ans中
                return;
            }

            for (int right = 0; right <= balance; right++) {
                path.push_back(i + right);

                //dfs(当前位置加上right个右括号和1个左括号,balance去掉right个右括号加上1个左括号)
                dfs(i + right + 1, balance - right + 1);
                
                path.pop_back();//回溯
            }
        };

        dfs(0, 0);
        return ans;
    }
};
相关推荐
狗凯之家源码网4 分钟前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php
不会就选b1 小时前
算法日常・每日刷题--<贪心>26
数据结构·算法
by209991 小时前
从结构体到对象:正式学习C++类的骨架、封装与this指针
c++·经验分享·学习
乌暮2 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
2601_962218612 小时前
C语言中三种库的编译和使用方法
开发语言·c++
传奇开心果编程2 小时前
中庸、有容、破执:仓颉编程语言的中华智慧结晶——三重哲学境界思辨
开发语言·华为·开源·软件工程
jimy12 小时前
派生类重写基类虚函数(二):override的作用
开发语言·c++
_upupup3 小时前
异常(C++)
java·开发语言·jvm
ebiobiz3 小时前
基于 GD32 Embedded Builder (GEB) 与 Nimmake 的 MCU 工程搭建指南
c++·python·单片机·嵌入式硬件·mcu
hetao17338373 小时前
2026-09-29 hetao1733837 的刷题记录
c++·算法