力扣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;
    }
};
相关推荐
寒月小酒2 小时前
3.23 OJ
数据结构·c++·算法
2501_924952692 小时前
模板编译期哈希计算
开发语言·c++·算法
xiaoye-duck2 小时前
C++ STL map 系列深度解析:从底层原理、核心接口到实战场景
开发语言·c++·stl
2201_758642642 小时前
嵌入式C++开发注意事项
开发语言·c++·算法
七夜zippoe2 小时前
WebAssembly与Python:在浏览器中运行Python
开发语言·python·wasm·webassembly·pyscript
闻哥2 小时前
MySQL InnoDB 缓存池(Buffer Pool)详解:原理、结构与链表管理
java·数据结构·数据库·mysql·链表·缓存·面试
AI科技星2 小时前
基于v≡c第一性原理的大统一力方程:严格推导、全维度验证与四大基本相互作用的统一
人工智能·线性代数·算法·机器学习·平面
小杍随笔2 小时前
【Rust 语言编程知识与应用:同步机制详解】
开发语言·算法·rust
sprite_雪碧2 小时前
枚举 / 搜索类算法(机试核心考点)
算法