《代码随想录》Ⅶ 回溯算法-组合 77. 组合

《代码随想录》Ⅶ 回溯算法-组合 77. 组合

努力学习!

题目:力扣链接

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

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

一、思想

这道题的核心思想是使用回溯算法来生成所有可能的组合。回溯算法是一种通过递归来探索所有可能解的算法,通常用于解决组合、排列、子集等问题。在这道题中,我们需要从范围 [1, n]​ 中选择 k​ 个数的所有可能组合。

具体来说,回溯算法的思路如下:

  1. 选择:从当前可选的数字中选择一个数字,并将其添加到当前路径中。
  2. 递归:继续选择下一个数字,直到路径中的数字个数达到 k​。
  3. 回溯:当路径中的数字个数达到 k​ 时,将当前路径添加到结果集中,并回溯到上一步,继续选择其他可能的数字。

通过这种方式,我们可以遍历所有可能的组合,并确保每个组合都是唯一的。

二、代码

class Solution
{
public:
    /**
     * 存放符合条件结果的集合
     */
    vector<vector<int>> result;
    /**
     * 用来存放符合条件结果
     */
    vector<int> path;
    /**
     * 回溯函数,用于生成所有可能的组合
     * @param n 数字上限
     * @param k 组合元素个数
     * @param startIndex 起始索引
     */
    void backtracking(int n, int k, int startIndex)
    {
        // 如果路径长度等于k,说明找到了一个符合条件的组合,将其添加到结果集中
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        // 遍历从startIndex到n的所有数字
        for (int i = startIndex; i <= n - (k - path.size()) + 1; ++i) {
            // 将当前数字添加到路径中
            path.push_back(i);
            // 递归调用,寻找下一个数字
            backtracking(n, k, i + 1);
            // 回溯,将当前数字从路径中移除
            path.pop_back();
        }
    }
    /**
     * 生成所有可能的组合
     * @param n 数字上限
     * @param k 组合元素个数
     * @return 所有可能的组合
     */
    vector<vector<int>> combine(int n, int k)
    {
        // 清空结果集和路径
        result.clear();
        path.clear();
        // 调用回溯函数,生成所有可能的组合
        backtracking(n, k, 1);
        // 返回结果集
        return result;
    }
};

三、代码解析

1. 算法工作原理分解

1.1 回溯函数 backtracking
  • 目的:递归地生成所有可能的组合。

  • 实现

    • 终止条件:当路径中的数字个数等于 k​ 时,将当前路径添加到结果集中,并返回。
    • 选择与递归:从 startIndex​ 开始遍历所有可能的数字,将当前数字添加到路径中,并递归调用 backtracking​ 函数,继续选择下一个数字。
    • 回溯:在递归调用返回后,将当前数字从路径中移除,以便尝试其他可能的数字。
1.2 组合生成函数 combine
  • 目的:初始化并调用回溯函数,生成所有可能的组合。

  • 实现

    • 初始化:清空结果集和路径。
    • 调用回溯函数:从数字 1​ 开始,调用 backtracking​ 函数生成所有可能的组合。
    • 返回结果:返回生成的所有组合。

2. 关键点说明

2.1 路径的选择与回溯
  • 路径path​ 变量用于存储当前正在生成的组合。
  • 选择:每次选择一个数字并将其添加到 path​ 中。
  • 回溯:在递归调用返回后,将当前数字从 path​ 中移除,以便尝试其他可能的数字。
2.2 结果集的存储
  • 结果集result​ 变量用于存储所有符合条件的组合。
  • 添加条件:当 path​ 中的数字个数等于 k​ 时,将 path​ 添加到 result​ 中。
2.3 剪枝
  • 所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置
  • 如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了

四、复杂度分析

  • 时间复杂度O(C(n, k))

    • 其中 C(n, k)​ 是从 n​ 个数字中选择 k​ 个数字的组合数。对于每个组合,我们需要 O(k)​ 的时间来生成,因此总时间复杂度为 O(C(n, k) * k)​。
  • 空间复杂度O(k)

    • 递归调用栈的深度最多为 k​,因此空间复杂度为 O(k)​。此外,结果集 result​ 的空间复杂度为 O(C(n, k) * k)​,但在通常情况下,我们只考虑递归栈的空间复杂度。

白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值