LeetCodehot 力扣热图100 括号生成
class Solution {
public:
vector<string> ans; // 存储所有有效括号组合的结果集
vector<string> generateParenthesis(int n) {
string s; // 当前构建的括号字符串
backtrack(0, 0, n, s); // 初始调用回溯函数
return ans;
}
void backtrack(int open, int close, int n, string &s) {
// 终止条件:当前字符串长度等于2n,说明得到一个有效组合
if (s.size() == 2 * n) {
ans.push_back(s);
return;
}
// 添加左括号的条件:已使用左括号数小于n
if (open < n) {
s.push_back('('); // 选择:添加左括号
backtrack(open + 1, close, n, s); // 递归探索后续可能性
s.pop_back(); // 回溯:撤销选择,尝试其他可能性
}
// 添加右括号的条件:已使用右括号数小于左括号数
if (close < open) {
s.push_back(')'); // 选择:添加右括号
backtrack(open, close + 1, n, s); // 递归探索后续可能性
s.pop_back(); // 回溯:撤销选择,尝试其他可能性
}
}
};
以下是当 `n=3` 时,回溯算法生成所有有效括号组合的详细运行步骤和回溯过程分析:
---
### **递归步骤详解(n=3)**
#### **初始状态**
- `open=0`, `close=0`, `s=""`
---
#### **步骤1:添加第一个左括号**
```plaintext
s = "("
open=1, close=0
递归调用 backtrack(1,0,3,"(")
```
---
#### **步骤2:添加第二个左括号**
```plaintext
s = "(("
open=2, close=0
递归调用 backtrack(2,0,3,"((")
```
---
#### **步骤3:添加第三个左括号**
```plaintext
s = "((("
open=3, close=0
递归调用 backtrack(3,0,3,"(((")
```
---
#### **步骤4:添加右括号(close < open)**
```plaintext
s = "((()"
open=3, close=1
递归调用 backtrack(3,1,3,"((()")
```
---
#### **步骤5:继续添加右括号**
```plaintext
s = "((())"
open=3, close=2
递归调用 backtrack(3,2,3,"((())")
```
---
#### **步骤6:完成第一个有效组合**
```plaintext
s = "((()))"
open=3, close=3
递归终止,保存结果 "((()))"
回溯到步骤5 → s = "((())"
```
---
#### **步骤7:回溯并探索其他路径**
```plaintext
回溯到步骤4 → s = "((()"
添加右括号:
s = "((())"
open=3, close=2 → 重复步骤5-6,但已处理
继续回溯到步骤3 → s = "((("
无法添加更多左括号(open=3),添加右括号:
s = "((()"
open=3, close=1 → 重复步骤4
```
---
#### **步骤8:回溯到步骤2 → s = "(("**
```plaintext
添加右括号(close < open):
s = "(()"
open=2, close=1
递归调用 backtrack(2,1,3,"(()")
```
---
#### **步骤9:添加左括号**
```plaintext
s = "(()("
open=3, close=1
递归调用 backtrack(3,1,3,"(()(")
添加右括号:
s = "(()()"
open=3, close=2
递归调用 backtrack(3,2,3,"(()()")
继续添加右括号:
s = "(()())"
保存结果 "(()())"
回溯到 s = "(()()" → 弹出右括号
```
---
#### **步骤10:回溯到 s = "(()("**
```plaintext
无法添加左括号(open=3),添加右括号:
s = "(()())" → 已保存
继续回溯到 s = "(()"
```
---
#### **步骤11:回溯到 s = "(()"**
```plaintext
添加右括号(close < open):
s = "(())"
open=2, close=2
递归调用 backtrack(2,2,3,"(())")
```
---
#### **步骤12:添加左括号**
```plaintext
s = "(())("
open=3, close=2
递归调用 backtrack(3,2,3,"(())(")
添加右括号:
s = "(())()"
保存结果 "(())()"
回溯到 s = "(())(" → 弹出左括号
```
---
#### **步骤13:回溯到 s = "(())"**
```plaintext
添加右括号:
s = "(())" → 无法继续(close=2 < open=2 不成立)
回溯到 s = "(()"
```
---
#### **步骤14:回溯到步骤1 → s = "("**
```plaintext
添加右括号(close < open):
s = "()"
open=1, close=1
递归调用 backtrack(1,1,3,"()")
```
---
#### **步骤15:添加左括号**
```plaintext
s = "()("
open=2, close=1
递归调用 backtrack(2,1,3,"()(")
添加左括号:
s = "()(("
open=3, close=1
递归调用 backtrack(3,1,3,"()((")
添加右括号:
s = "()(()"
→ 继续递归直到生成 "()(())"
保存结果 "()(())"
回溯到 s = "()(()" → 弹出右括号
```
---
#### **步骤16:回溯到 s = "()(("**
```plaintext
添加右括号:
s = "()(()" → 重复
继续回溯到 s = "()("
添加右括号:
s = "()()"
open=2, close=2
递归调用 backtrack(2,2,3,"()()")
```
---
#### **步骤17:添加左括号**
```plaintext
s = "()()("
open=3, close=2
递归调用 backtrack(3,2,3,"()()(")
添加右括号:
s = "()()()"
保存结果 "()()()"
```
---
### **最终结果**
所有有效组合(共5种):
1. `((()))`
2. `(()())`
3. `(())()`
4. `()(())`
5. `()()()`
---
### **关键回溯路径图(简化版)**
```plaintext
开始(空字符串)
|
├─ 添加'(' → "("
│ ├─ 添加'(' → "(("
│ │ ├─ 添加'(' → "((("
│ │ │ └─ 添加')' → "((()" → ... → "((()))"
│ │ └─ 添加')' → "(()" → ... → "(()())", "(())()"
│ └─ 添加')' → "()"
│ ├─ 添加'(' → "()(" → ... → "()(())", "()()()"
│ └─ 添加')' → 无效(剪枝)
└─ 添加')' → 无效(剪枝)
```
---
### **算法核心逻辑**
1. **左括号优先**:只要左括号未达 `n`,优先添加左括号。
2. **右括号限制**:右括号数量必须小于左括号时才能添加。
3. **回溯剪枝**:通过 `s.pop_back()` 撤销无效尝试,避免重复路径。
此过程通过递归和回溯系统地探索所有可能的组合,并利用剪枝条件确保每一步都是有效的。