Update Leetcode 题解 - 搜索.md

新增括号生成题解,补充排列与子集类回溯的复杂度。
This commit is contained in:
CyC2018
2026-10-31 00:23:18 +08:00
parent 376e324a27
commit 03d473bb79
+48
View File
@@ -27,6 +27,7 @@
* [13. 分割字符串使得每个部分都是回文数](#13-分割字符串使得每个部分都是回文数)
* [14. 数独](#14-数独)
* [15. N 皇后](#15-n-皇后)
* [16. 括号生成](#16-括号生成)
* [附录:实测](#附录实测)
* [A1. 岛屿数量的递归深度](#a1-岛屿数量的递归深度)
<!-- GFM-TOC -->
@@ -644,6 +645,8 @@ backtracking(当前选择):
[岛屿数量](#2-矩阵中的连通分量数目)的标记表示格子已经处理过,不再撤销;[在矩阵中寻找字符串](#3-在矩阵中寻找字符串)的标记表示格子在当前路径上,换一条路径时要撤销。
以 N 个互不相同的元素为例:排列有 N! 个,每个复制 O(N),总时间 O(N·N!);子集有 2<sup>N</sup> 个,总时间 O(N·2<sup>N</sup>)。这两类问题的额外空间是递归深度 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<String> generateParenthesis(int n) {
List<String> combinations = new ArrayList<>();
backtracking(new StringBuilder(), 0, 0, n, combinations);
return combinations;
}
private void backtracking(StringBuilder prefix, int open, int close, int n, List<String> 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(4<sup>n</sup> / √n)。
## 附录:实测
### A1. 岛屿数量的递归深度