This commit is contained in:
parent
a5dd365883
commit
b4bc826729
15
include/s0077_combinations.hpp
Normal file
15
include/s0077_combinations.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef S0077_COMBINATIONS_HPP
|
||||
#define S0077_COMBINATIONS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class S0077 {
|
||||
public:
|
||||
vector<vector<int>> combine(int n, int k);
|
||||
};
|
||||
|
||||
#endif
|
@ -38,6 +38,11 @@
|
||||
- [遍历](./btree_iter.md)
|
||||
- [二叉搜索树](./bstree.md)
|
||||
|
||||
# 回溯
|
||||
|
||||
- [总结](./backtrack.md)
|
||||
- [组合问题](./combinations.md)
|
||||
|
||||
# STL
|
||||
|
||||
- [总结](./stl.md)
|
||||
|
28
notes/src/backtrack.md
Normal file
28
notes/src/backtrack.md
Normal file
@ -0,0 +1,28 @@
|
||||
# 总结
|
||||
|
||||
使用场景:
|
||||
|
||||
- 如果解决一个问题需要多个步骤,而每个步骤有多个可能的结果,题目又要求我们找出所有可能的结果,那么这个时候可以考虑回溯法。
|
||||
- 回溯法本质是在一棵树上进行深度优先遍历
|
||||
|
||||
算法设计:
|
||||
|
||||
关键就是要学会分析和画图,然后确定传入参数。只要传入参数确定了代码框架就确定了 (返回值一般是 `void`):
|
||||
|
||||
- 得有一个 `&result` 来存放结果
|
||||
- 每一层的节点所包含的数据是什么,需要一系列的参数来描述当前节点的状态(传入的参数实际上是上一轮迭代的数据)。
|
||||
- 终止条件是什么?回溯法中的每个节点并不是真的树状节点,没有 `nullptr` ,因此用空指针来判断是否到了叶子结点并不合理,需要其它的一些方法来确定是否到达叶子节点,比如高度。
|
||||
- 每次迭代要在上一次迭代的基础上进行哪些操作?需要哪些参数才能完成这些操作?
|
||||
|
||||
复杂度分析:
|
||||
|
||||
- 时间复杂度:最长路径长度 × 搜索树的节点数
|
||||
- 空间复杂度:一个节点所需要的空间 × 搜索树的节点数
|
||||
|
||||
分类:
|
||||
|
||||
- 组合问题:N 个数里面按一定规则找出 k 个数的集合
|
||||
- 切割问题:一个字符串按一定规则有几种切割方式
|
||||
- 子集问题:一个 N 个数的集合里有多少符合条件的子集
|
||||
- 排列问题:N 个数按一定规则全排列,有几种排列方式
|
||||
- 棋盘问题:N 皇后,解数独等等
|
19
notes/src/combinations.md
Normal file
19
notes/src/combinations.md
Normal file
@ -0,0 +1,19 @@
|
||||
# 组合问题
|
||||
|
||||
## [77. 组合](https://leetcode.cn/problems/combinations/description/)
|
||||
|
||||
![combinations](https://paste.sainnhe.dev/Cytj.png)
|
||||
|
||||
每个节点存储的数据是什么?是一个 `vector<int>` 类型的数据,代表当前节点的路径。
|
||||
|
||||
下一个节点的路径需要基于上一个节点的路径来获得,因此传入参数应该有一个 `vector<int> path`。另外,还需要有一个 `vector<vector<int>> &result` 用来存放结果。
|
||||
|
||||
终止条件是什么?回溯法中的每个节点并不是真的树状节点,没有 `nullptr` ,因此用空指针来判断是否到了叶子节点并不合理。
|
||||
|
||||
本题中我们可以通过高度来判断是否达到了叶子节点,如果 `path.size() == k` 则说明到达了叶子节点,则停止迭代,并把当前路径添加到结果变量中。
|
||||
|
||||
因此我们还需要高度 `k`,`int k` 也应该是一个传入参数。
|
||||
|
||||
为了防止重复,我们需要在 `[1, n]` 中的一个子区间 `[begin, n]` 中选择一个数,`[1, begin]` 是我们已经选过了的,因此我们需要 `int n` 和 `int begin` 来作为传入参数。
|
||||
|
||||
在每次迭代中,我们从 `[begin, n]` 中挨个选一个数加到上一轮迭代传递进来的 `path` 中,然后进行下一轮迭代。
|
27
src/s0077_combinations.cpp
Normal file
27
src/s0077_combinations.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "s0077_combinations.hpp"
|
||||
|
||||
void combineDFS(int n, int k, int begin, vector<int> &path,
|
||||
vector<vector<int>> &result) {
|
||||
// 当 path 长度等于 k 时停止迭代,并将加入结果
|
||||
if (path.size() == k) {
|
||||
result.push_back(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历可能的搜索起点
|
||||
for (int i = begin; i <= n; ++i) {
|
||||
// 将 i 加入路径
|
||||
path.push_back(i);
|
||||
// 下一轮搜索
|
||||
combineDFS(n, k, i + 1, path, result);
|
||||
// 回溯,撤销处理的节点
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
vector<vector<int>> S0077::combine(int n, int k) {
|
||||
vector<vector<int>> result;
|
||||
vector<int> path;
|
||||
combineDFS(n, k, 1, path, result);
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user