组合问题(对应力扣77)

题⽬链接:https://leetcode-cn.com/problems/combinations/

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 示例: 输⼊: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4],

思路

看到k=2,那我就直接可以两层for循环了

int n = 4;
for(int i = 1; i <= n; i++) {
    for(int j = i+1; j <= n; j++) {
        cout<< i << " " << j << endl;
    }
}
 

如果输入 n=100,k=3;当然可以三层循环了;但是如果k=50呢,50层for循环,啊这~就不好整了。

那么回溯法就⽤递归来 解决嵌套层数的问题。 递归来做层叠嵌套(可以理解是开k层for循环),每⼀次的递归中嵌套⼀个for循环,那么递归就可以⽤ 于解决多层嵌套循环的问题了。

我们知道回溯法可以看成一个树(上篇文章有讲

组合问题(对应力扣77)

 从上述图中可以清楚看出,n相当于深度,k相当于宽度,从根节点开始遍历,搜素子节点。

定义两个全局变量,用来存放单一,集合结果

vector<vector<int>> result; //存放结果集合
vector<int> tmp;           //存放单一结果

 那么还需定义一个startNext,因为第一层递归到了[1,2,3,4]取1,下一层[2,3,4]中取数靠的就是startNext

vector<vector<int>> result; // 存放符合条件结果的集合
vector<int> path; // ⽤来存放符合条件单⼀结果
void backtracking(int n, int k, int startNext)

如果path数组大小等于k,就找到停止。

if (path.size() == k) {
 result.push_back(path);
 return;
}

要遍历整个数需横纵都得遍历 ,for循环是横向遍历,递归是纵向遍历

for循环每次从startNext开始遍历,然后⽤path保存取到的节点i

for(int i = startNext; i <= n; i++) {  //横向遍历
    path.push_back(i);   //处理节点
    backtracking(n, k, i+1);   //递归,纵向
    path.pop_back();    回溯,撤销处理的节点
}

可以看出backtracking(递归函数)通过不断调⽤⾃⼰⼀直往深处遍历,总会遇到叶⼦节点,遇到了叶 ⼦节点就要返回。 backtracking的下⾯部分就是回溯的操作了,撤销本次处理的结果。

优化

图中每⼀个节点(图中为矩形),就代表本层的⼀个for循环,那么每⼀层的for循环从第⼆个数开始遍 历的话,都没有意义,都是⽆效遍历

所以,可以剪枝的地⽅就在递归中每⼀层的for循环所选择的起始位置。 如果for循环选择的起始位置之后的元素个数 已经不⾜ 我们需要的元素个数了。

接下来看⼀下优化过程如下:

1. 已经选择的元素个数:path.size();

2. 还需要的元素个数为: k - path.size();

3. 在集合n中⾄多要从该起始位置 : n - (k - path.size()) + 1,开始遍历

为什么有个+1呢,因为包括起始位置,我们要是⼀个左闭的集合。

举个例⼦,n = 4,k = 3, ⽬前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。 从2开始搜索都是合理的,可以是组合[2, 3, 4]。

所以我们只需在for循环i<=n的地方换成n-(k-path,size())+1就欧克了

学习于代码随想录

上一篇:77. 组合


下一篇:77_初识Java_static_学习