diff --git a/notes/Leetcode 题解 - 搜索.md b/notes/Leetcode 题解 - 搜索.md
index 29600e69..155a05f1 100644
--- a/notes/Leetcode 题解 - 搜索.md
+++ b/notes/Leetcode 题解 - 搜索.md
@@ -27,6 +27,7 @@
* [13. 分割字符串使得每个部分都是回文数](#13-分割字符串使得每个部分都是回文数)
* [14. 数独](#14-数独)
* [15. N 皇后](#15-n-皇后)
+ * [16. 括号生成](#16-括号生成)
* [附录:实测](#附录实测)
* [A1. 岛屿数量的递归深度](#a1-岛屿数量的递归深度)
@@ -644,6 +645,8 @@ backtracking(当前选择):
[岛屿数量](#2-矩阵中的连通分量数目)的标记表示格子已经处理过,不再撤销;[在矩阵中寻找字符串](#3-在矩阵中寻找字符串)的标记表示格子在当前路径上,换一条路径时要撤销。
+以 N 个互不相同的元素为例:排列有 N! 个,每个复制 O(N),总时间 O(N·N!);子集有 2N 个,总时间 O(N·2N)。这两类问题的额外空间是递归深度 O(N)(不计结果)。
+
### 1. 数字键盘组合
17\. Letter Combinations of a Phone Number (Medium)
@@ -1400,6 +1403,51 @@ private void backtracking(int row) {
}
```
+### 16. 括号生成
+
+22\. Generate Parentheses (Medium)
+
+[Leetcode](https://leetcode.com/problems/generate-parentheses/description/) / [力扣](https://leetcode-cn.com/problems/generate-parentheses/description/)
+
+```text
+Input: n = 3
+Output: ["((()))","(()())","(())()","()(())","()()()"]
+```
+
+从左到右逐个放括号,每一步只有两种选择,用两条规则剪枝:
+
+- 左括号用了不到 n 个时,才能放左括号;
+- 右括号的个数少于左括号时,才能放右括号,这样任何前缀中右括号都不会多于左括号。
+
+放满 2n 个字符时左右括号各 n 个,得到的就是合法的组合。例如 n = 2 时,第一个字符只能是左括号,第二个字符分出 "((" 和 "()" 两支,之后各自只剩一种放法,得到 "(())" 和 "()()"。
+
+```java
+public List generateParenthesis(int n) {
+ List combinations = new ArrayList<>();
+ backtracking(new StringBuilder(), 0, 0, n, combinations);
+ return combinations;
+}
+
+private void backtracking(StringBuilder prefix, int open, int close, int n, List combinations) {
+ if (prefix.length() == 2 * n) {
+ combinations.add(prefix.toString());
+ return;
+ }
+ if (open < n) {
+ prefix.append('(');
+ backtracking(prefix, open + 1, close, n, combinations);
+ prefix.deleteCharAt(prefix.length() - 1);
+ }
+ if (close < open) {
+ prefix.append(')');
+ backtracking(prefix, open, close + 1, n, combinations);
+ prefix.deleteCharAt(prefix.length() - 1);
+ }
+}
+```
+
+满足这两条规则的前缀总能补成合法的组合,搜索不会走进死路。结果个数是卡特兰数 C(2n, n) / (n + 1),n = 3 时 5 个,n = 8 时 1430 个;每个结果长 2n,总时间 O(n·C(2n, n) / (n + 1)),约为 O(4n / √n)。
+
## 附录:实测
### A1. 岛屿数量的递归深度