【力扣-回溯】1、组合(77)组合总和III(216) 77

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战

77. 组合

题目描述

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

1
2
3
4
5
6
7
8
9
10
ini复制代码输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

示例 2:

1
2
lua复制代码输入: n = 1, k = 1
输出: [[1]]

解析

回溯法就是使用暴力法枚举出所有的可能,递归回溯相辅相成,回溯可以看成一棵树。以本题为例,树的深度为 n , 宽度为 k 。所以横向遍历的次数为 k , 纵向遍历的次数为 n 。
回溯三部曲:

  • 1、确定递归函数的参数与返回值
    • 求组合数,所以不考虑元素的顺序
    • 使用一个数组vector<int> path存放每次遍历的结果
    • 使用数组vector<vector<int>> result存放 path
    • 使用int startIndex来标记递归的每一层遍历开始的索引
  • 2、确定终止条件
    • 如果path已经存入了k个元素,则终止此次遍历
  • 3、确定单层递归的逻辑
    • 每层遍历时,将遍历的元素加入到path
    • 进行回溯

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
c++复制代码class Solution
{
public:
vector<vector<int>> combine(int n, int k)
{
backtracking(n, k, 1);
return result;
}

private:
// 使用两个全局变量存放结果
// 存放最终的结果
vector<vector<int>> result;
// 存放单层递归的结果
vector<int> path;

// 回溯算法
// startIndex用来控制开始遍历的索引,遍历的范围逐渐缩小
void backtracking(int n, int k, int startIndex)
{
// 递归的终止条件
// 当path的 大小等于 k 时,将path数据存入到 结果数组中
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(); // 回溯,撤销处理的节点
}
}
};

剪枝操作

在单层for循环中,如果剩余的元素不足以和已经选取的元素组成K个,那么后面的遍历就是不必要的

  • 已经选取元素个数:path.size()
  • 还需要选取元素个数: k - path.size()
  • 所以,遍历的起始索引必须满足 startIndex<=n - (k - path.size())+1
  • 修改后的for循环:*
1
2
3
4
c++复制代码for(int i = startIndex;i<=n-(k-path.size())+1;i++)
{
... ...
}

216. 组合总和 III

题目描述

找出所有相加之和为 nk 个数的组合 ** **组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1:

1
2
lua复制代码输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

1
2
ini复制代码输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

解析

与上面的组合题思路相似,这里是求 k 个数的和等于 n ,在判断条件上稍作改动。具体内容查看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
c++复制代码class Solution
{
public:
vector<vector<int>> combinationSum3(int n, int k)
{
backtracking(n, k, 1);
return result;
}

private:
vector<vector<int>> result;
vector<int> path;

// 使用减法逐步减小n的值
void backtracking(int n, int k, int startIndex)
{
// 剪枝操作
if (n < 0)
{
return;
}
// 如果path的大小满足条件,并且path中的和为 n
if (path.size() == k && n == 0)
{
result.push_back(path);
return;
}

for (int i = startIndex; i <= 9; i++)
{
// 每次遍历到一个数,就将n的值减去这个数
n -= i;
// 将 i 添加到 path 中
path.push_back(i);
// 递归处理
backtracking(n, k, i + 1);
n += i; // 回溯
path.pop_back(); // 回溯
}
}
};

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%