1.回溯算法
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。
1.1题目分类
回溯算法主要解决组合、分割、子集、排列等问题。本博客将记录代码随想录回溯专题下的组合问题,用于日后的回忆与复习。
2.组合问题
组合问题通常用于求解一个集合中满足某个要求的子集了。
区别于排列问题,在组合问题中,给出集合{a,b}和{b,a},组合只关心组内元素不关心顺序,因此视为一个解。然而同一情况下,排列问题则视为两个解。
2.1问题描述
先从一个简单的小例子开始,给定五个不同颜色的小球,每种颜色的小球只能选取一次,如果两两配对组合,能够得到多少种组合。
图1.1
如图1.1所示,每一条黑线为一种组合方案。每次选取时,可选择的数目上限为给定集合的大小。当第一次选取到紫色后,第二次没有可选择的小球,因此舍去。
通过观察可以发现,进入下一层的选择后,选择空间变为上层选取结果的右侧小球。举例,第一次选择红球后,第二次选取的选择空间变为红球右侧(黄、蓝、绿、紫)。注意:由于限制每个小球只能选取一次,因此不能包括自己本身,如果没有这一限制,则红球也在第二次的选择空间内。图1.2直观展示这一过程。
图1.2
为什么会是这一结果呢?我们不妨假设第二次选取不受这样的限制。以图1.1中第一次选取黄色小球为例,倘若此时第二次选取了红球,此时组合的结果为{黄球,红球},这一组合方式不就与{红球,黄球}重复了嘛。因此我们需要一个标记,记录下一次选取时的开始位置。如果题目要求同一小球只能选取一次,那么下次选取的开始位置则为当前选取的位置+1。
2.2解题思路
回溯问题的思路可以抽象成一个树形结构,我们思考题目时可以将解题过程通过一颗N叉树体现出来。不同的题目要求只不过是细节上的差异。
3.算法题
3.1 组合
题目链接
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
题解一(不包含剪枝)
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n, int k, int startIndex)
{
if(path.size() == k)
{
result.push_back(path);
return;
}
for(int i = startIndex; i <= n; i++)
{
path.push_back(i);
backtracking(n, k, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> combine(int n, int k)
{
path.clear();
result.clear();
backtracking(n, k, 1);
return result;
}
};
题解二(包含剪枝):
class Solution {
private:
vector<vector<int>> result; //result