diff --git a/include/s0077_combinations.hpp b/include/s0077_combinations.hpp index 4c59a11..ee92d93 100644 --- a/include/s0077_combinations.hpp +++ b/include/s0077_combinations.hpp @@ -1,8 +1,6 @@ #ifndef S0077_COMBINATIONS_HPP #define S0077_COMBINATIONS_HPP -#include -#include #include using namespace std; diff --git a/notes/src/combinations.md b/notes/src/combinations.md index 20416fd..77e724d 100644 --- a/notes/src/combinations.md +++ b/notes/src/combinations.md @@ -17,3 +17,54 @@ 为了防止重复,我们需要在 `[1, n]` 中的一个子区间 `[begin, n]` 中选择一个数,`[1, begin]` 是我们已经选过了的,因此我们需要 `int n` 和 `int begin` 来作为传入参数。 在每次迭代中,我们从 `[begin, n]` 中挨个选一个数加到上一轮迭代传递进来的 `path` 中,然后进行下一轮迭代。 + +```cpp +void combineDFS(int n, int k, int begin, vector &path, + vector> &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(); + } +} +``` + +我们现在来看看能不能优化。 + +![optimization](https://paste.sainnhe.dev/NzcF.png) + +在上图的这种情况中,每一层其实都可以剪掉一些不可能的分支,我们可以对每一层循环的终止条件进行限制,从而剪枝。 + +优化后的代码如下: + +```cpp +void combineDFS(int n, int k, int begin, vector &path, + vector> &result) { + // 当 path 长度等于 k 时停止迭代,并将加入结果 + if (path.size() == k) { + result.push_back(path); + return; + } + + // 遍历可能的搜索起点 + // 在这一步中,每一次循环都可以对末尾进行限制来剪枝 + for (int i = begin; i <= n - (k - path.size()) + 1; ++i) { + // 将 i 加入路径 + path.push_back(i); + // 下一轮搜索 + combineDFS(n, k, i + 1, path, result); + // 回溯,撤销处理的节点 + path.pop_back(); + } +} +``` diff --git a/src/s0077_combinations.cpp b/src/s0077_combinations.cpp index f30bb06..8036828 100644 --- a/src/s0077_combinations.cpp +++ b/src/s0077_combinations.cpp @@ -9,7 +9,8 @@ void combineDFS(int n, int k, int begin, vector &path, } // 遍历可能的搜索起点 - for (int i = begin; i <= n; ++i) { + // 在这一步中,每一次循环都可以对末尾进行限制来剪枝 + for (int i = begin; i <= n - (k - path.size()) + 1; ++i) { // 将 i 加入路径 path.push_back(i); // 下一轮搜索