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

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

77. 组合

题目描述

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

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

示例 1:

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

示例 2:

输入: 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
    • 进行回溯

代码

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循环:

for(int i = startIndex;i<=n-(k-path.size())+1;i++)
{
    ... ...
}
复制代码

216. 组合总和 III

题目描述

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

说明:

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

示例 1:

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

示例 2:

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

解析

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

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(); // 回溯
        }
    }
};
复制代码

猜你喜欢

转载自juejin.im/post/7033568931814047758