力扣第三十九题——组合总和

内容介绍

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

完整代码

 int candidatesSize_tmp;

int ansSize;

int combineSize;

int* ansColumnSize;

void dfs(int* candidates, int target, int** ans, int* combine, int idx) {
    if (idx == candidatesSize_tmp) {
        return;
    }
    if (target == 0) {
        int* tmp = malloc(sizeof(int) * combineSize);
        for (int i = 0; i < combineSize; ++i) {
            tmp[i] = combine[i];
        }
        ans[ansSize] = tmp;
        ansColumnSize[ansSize++] = combineSize;
        return;
    }
    // 直接跳过
    dfs(candidates, target, ans, combine, idx + 1);
    // 选择当前数
    if (target - candidates[idx] >= 0) {
        combine[combineSize++] = candidates[idx];
        dfs(candidates, target - candidates[idx], ans, combine, idx);
        combineSize--;
    }
}

int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
    candidatesSize_tmp = candidatesSize;
    ansSize = combineSize = 0;
    int** ans = malloc(sizeof(int*) * 1001);
    ansColumnSize = malloc(sizeof(int) * 1001);
    int combine[2001];
    dfs(candidates, target, ans, combine, 0);
    *returnSize = ansSize;
    *returnColumnSizes = ansColumnSize;
    return ans;
}

思路详解

问题概述

组合总和问题是一个典型的回溯算法问题,其目标是从候选数组中找出所有可能的组合,这些组合的和等于给定的目标值。

代码详解

int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
    // 初始化一些变量
    candidatesSize_tmp = candidatesSize; // 用于保存候选数组的大小
    ansSize = combineSize = 0; // 初始化结果数组的大小和当前组合的大小
    int** ans = malloc(sizeof(int*) * 1001); // 分配结果数组的空间
    ansColumnSize = malloc(sizeof(int) * 1001); // 分配结果数组列大小的空间
    int combine[2001]; // 初始化组合数组
    dfs(candidates, target, ans, combine, 0); // 开始递归查找组合
    *returnSize = ansSize; // 设置返回的结果数组大小
    *returnColumnSizes = ansColumnSize; // 设置返回的结果数组列大小
    return ans; // 返回结果数组
}

void dfs(int* candidates, int target, int** ans, int* combine, int idx) {
    // 如果索引已经达到候选数组的大小,说明当前路径已经遍历完成
    if (idx == candidatesSize_tmp) {
        return;
    }
    // 如果当前目标值等于0,说明找到了一个有效的组合
    if (target == 0) {
        int* tmp = malloc(sizeof(int) * combineSize); // 创建一个临时数组来保存当前组合
        for (int i = 0; i < combineSize; ++i) {
            tmp[i] = combine[i]; // 复制当前组合到临时数组
        }
        ans[ansSize] = tmp; // 将临时数组添加到结果数组中
        ansColumnSize[ansSize++] = combineSize; // 更新结果数组列大小
        return; // 返回上一层递归
    }
    // 如果不满足直接跳过当前元素
    dfs(candidates, target, ans, combine, idx + 1);
    // 如果当前元素小于目标值,可以选择当前元素
    if (target - candidates[idx] >= 0) {
        combine[combineSize++] = candidates[idx]; // 将当前元素添加到组合中
        dfs(candidates, target - candidates[idx], ans, combine, idx); // 递归查找剩余的组合
        combineSize--; // 回溯,撤销当前元素的选择
    }
}

关键步骤解释

  1. 初始化

    • combinationSum函数中,首先初始化一些变量,包括结果数组的大小和当前组合的大小。
  2. 递归函数定义

    • dfs函数是递归函数,它接受候选数组、目标值、结果数组、当前组合数组和当前索引作为参数。
  3. 递归终止条件

    • 如果索引已经达到候选数组的大小,说明当前路径已经遍历完成,函数返回。
    • 如果当前目标值等于0,说明找到了一个有效的组合,函数将当前组合保存到结果数组中,并返回。
  4. 递归调用

    • 如果不满足直接跳过当前元素的条件,函数递归调用自身,尝试下一个元素。
    • 如果当前元素小于目标值,函数将当前元素添加到组合中,递归调用自身,尝试剩余的组合。
    • 回溯时,撤销当前元素的选择,继续递归查找其他可能的组合。
  5. 结果保存

    • 在递归函数中,当找到一个有效的组合时,将当前组合保存到结果数组中,并更新结果数组的大小。
  6. 返回结果

    • combinationSum函数中,返回结果数组和结果数组列大小。

知识点精炼

 

  1. 递归算法

    • 使用递归方法dfs来遍历所有可能的组合。
  2. 回溯算法

    • dfs函数中应用回溯,撤销当前元素的选择以探索其他可能性。
  3. 数组与指针

    • 使用数组combine来存储当前组合的元素,使用指针combineSize来跟踪组合的大小。
  4. 动态内存分配

    • 使用malloc为结果数组和临时数组分配内存。
  5. 状态更新

    • 在递归函数中,使用combineSizeansSize来跟踪当前组合的大小和结果数组的大小。
  6. 数组索引操作

    • 通过数组索引来访问和更新当前组合和结果数组。
  7. 条件判断

    • 使用条件判断来确定是否跳过当前元素或者将当前元素添加到组合中。
  8. 子序列计数

    • 通过combineSize来计算当前组合的大小。
  9. 结果保存

    • 将有效的组合保存到结果数组中,并更新结果数组的大小。
  10. 返回值

    • 最终返回结果数组和结果数组列大小。

通过这些知识点,代码有效地解决了“组合总和”问题,展示了递归、回溯、以及高效状态管理的算法技巧。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值