s0077
All checks were successful
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
Sainnhe Park 2023-02-01 18:09:19 +08:00
parent a5dd365883
commit b4bc826729
5 changed files with 94 additions and 0 deletions

View 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

View File

@ -38,6 +38,11 @@
- [遍历](./btree_iter.md)
- [二叉搜索树](./bstree.md)
# 回溯
- [总结](./backtrack.md)
- [组合问题](./combinations.md)
# STL
- [总结](./stl.md)

28
notes/src/backtrack.md Normal file
View File

@ -0,0 +1,28 @@
# 总结
使用场景:
- 如果解决一个问题需要多个步骤,而每个步骤有多个可能的结果,题目又要求我们找出所有可能的结果,那么这个时候可以考虑回溯法。
- 回溯法本质是在一棵树上进行深度优先遍历
算法设计:
关键就是要学会分析和画图,然后确定传入参数。只要传入参数确定了代码框架就确定了 (返回值一般是 `void`)
- 得有一个 `&result` 来存放结果
- 每一层的节点所包含的数据是什么,需要一系列的参数来描述当前节点的状态(传入的参数实际上是上一轮迭代的数据)。
- 终止条件是什么?回溯法中的每个节点并不是真的树状节点,没有 `nullptr` ,因此用空指针来判断是否到了叶子结点并不合理,需要其它的一些方法来确定是否到达叶子节点,比如高度。
- 每次迭代要在上一次迭代的基础上进行哪些操作?需要哪些参数才能完成这些操作?
复杂度分析:
- 时间复杂度:最长路径长度 × 搜索树的节点数
- 空间复杂度:一个节点所需要的空间 × 搜索树的节点数
分类:
- 组合问题N 个数里面按一定规则找出 k 个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个 N 个数的集合里有多少符合条件的子集
- 排列问题N 个数按一定规则全排列,有几种排列方式
- 棋盘问题N 皇后,解数独等等

19
notes/src/combinations.md Normal file
View 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` 中,然后进行下一轮迭代。

View 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;
}