leetcode216.组合总和III、40.组合总和II、39.组合总和

216.组合总和III

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

只使用数字1到9

每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7

输出: [[1,2,4]]

解释:

1 + 2 + 4 = 7

没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9

输出: [[1,2,6], [1,3,5], [2,3,4]]

解释:

1 + 2 + 6 = 9

1 + 3 + 5 = 9

2 + 3 + 4 = 9

没有其他符合的组合了。

示例 3:

输入: k = 4, n = 1

输出: []

解释: 不存在有效的组合。

在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

解题思路

本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。

本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。

例如 k = 2,n = 4的话,就是在集合[1,2,3,4,5,6,7,8,9]中求 k(个数) = 2, n(和) = 4的组合。

选取过程如图:

回溯三部曲

1.确定递归函数参数

和77. 组合一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。

这里我依然定义path 和 result为全局变量。

至于为什么取名为path?从上面树形结构中,可以看出,结果其实就是一条根节点到叶子节点的路径。

接下来还需要如下参数:

n(int)目标和,也就是题目中的n。

k(int)就是题目中要求k个数的集合。

sum(int)为已经收集的元素的总和,也就是path里元素的总和。

startIndex(int)为下一层for循环搜索的起始位置。

2、确定终止条件

在上面已经说了,k其实就已经限制树的深度,因为就取k个元素,树再往下深了没有意义。

所以如果pathTop和 k相等了,就终止。

如果此时path里收集到的元素和(sum) 和n(就是题目描述的n)相同了,就用result收集当前的结果。

所以 终止代码如下:

c 复制代码
if(pathTop==k){
        if(sum==n){
            int* tmp=(int*)malloc(sizeof(int)*k);
            for(int i=0;i<k;i++){
                tmp[i]=path[i];
            }
            ans[ansTop++]=tmp;
        }
        return;
    }
3、单层搜索过程

本题和77. 组合区别之一就是集合固定的就是9个数[1,...,9],所以for循环固定i<=9

c 复制代码
for(int i=startIdx;i<=9;i++){
        sum+=i;
        path[pathTop++]=i;
         backtracking(n,k,sum,i+1);
          sum-=i;
          pathTop--;
    }

代码

未剪枝

c 复制代码
int *path;
 int pathTop;
 int **ans;
 int ansTop;
 void backtracking(int n,int k,int sum,int startIdx){
    if(pathTop==k){
        if(sum==n){
            int* tmp=(int*)malloc(sizeof(int)*k);
            for(int i=0;i<k;i++){
                tmp[i]=path[i];
            }
            ans[ansTop++]=tmp;
        }
        return;
    }
    for(int i=startIdx;i<=9;i++){
        sum+=i;
        path[pathTop++]=i;
         backtracking(n,k,sum,i+1);
          sum-=i;
          pathTop--;
    }

 }
int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes) {
    path=(int*)malloc(sizeof(int)*k);
    ans=(int**)malloc(sizeof(int*)*2000);
    pathTop=ansTop=0;
    backtracking(n,k,0,1);
    *returnSize=ansTop;
    *returnColumnSizes=(int*)malloc(sizeof(int)*ansTop);
    for(int i=0;i<ansTop;i++)
         (*returnColumnSizes)[i]=k;
        return ans;
}

剪枝

c 复制代码
int *path;
 int pathTop;
 int **ans;
 int ansTop;
 void backtracking(int n,int k,int sum,int startIdx){
    if(sum>n) return;//剪枝,减去和大于n的分支
    if(pathTop==k){
        if(sum==n){
            int* tmp=(int*)malloc(sizeof(int)*k);
            for(int i=0;i<k;i++){
                tmp[i]=path[i];
            }
            ans[ansTop++]=tmp;
        }
        return;
    }
    for(int i=startIdx;i<=9-(k-pathTop)+1;i++){//剪枝,剪去不满足k个数的分支
        sum+=i;
        path[pathTop++]=i;
         backtracking(n,k,sum,i+1);
          sum-=i;
          pathTop--;
    }

 }
int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes) {
    path=(int*)malloc(sizeof(int)*k);
    ans=(int**)malloc(sizeof(int*)*2000);
    pathTop=ansTop=0;
    backtracking(n,k,0,1);
    *returnSize=ansTop;
    *returnColumnSizes=(int*)malloc(sizeof(int)*ansTop);
    for(int i=0;i<ansTop;i++)
         (*returnColumnSizes)[i]=k;
        return ans;
}

40.组合总和II

给定一个可能有重复数字的整数数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,

输出:

\[1,1,6\], \[1,2,5\], \[1,7\], \[2,6

]

示例 2:

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

输出:

\[1,2,2\], \[5

]

题目解析

元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。所以我们要去重的是同一树层上的"使用过",同一树枝上的都是一个组合里的元素,不用去重。树层去重的话,需要对数组排序!

回溯三部曲

1、递归函数参数

和77. 组合一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。

这里我依然定义path 和 result为全局变量。length存放每个组合的长度。

至于为什么取名为path?从上面树形结构中,可以看出,结果其实就是一条根节点到叶子节点的路径。

接下来还需要如下参数:

target(int)目标和,也就是题目中的n。

candidates(int*)数组

candidatesSize(int)就是题目中数组的大小。

sum(int)为已经收集的元素的总和,也就是path里元素的总和。

startIndex(int)为下一层for循环搜索的起始位置。

2、递归终止条件

终止条件为 sum > target 和 sum == target。

3、单层搜索的逻辑

要去重的是"同一树层上的使用过",如何判断同一树层上元素(相同的元素)是否使用过了呢。

如果candidates[i] == candidates[i - 1] 并且 i>starIdx,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。

此时for循环里就应该做continue的操作。

c 复制代码
int *path;
 int pathTop;
 int **ans;
 int ansTop;
 int* length;
 int cmp(const void* a1, const void* a2) {
    return *((int*)a1) - *((int*)a2);
}
void backtracking(int* candidates,int candidatesSize,int target,int sum,int startIdx){
    if(sum>=target){
        if(sum==target){
            int* tmp=(int*)malloc(sizeof(int)*pathTop);
            for(int j=0;j<pathTop;j++)
            tmp[j]=path[j];
            length[ansTop]=pathTop;//存储当前组合的长度
             ans[ansTop++]=tmp;
        }
        return;
    }
    for(int i=startIdx;i<candidatesSize;i++){
        if(i>startIdx&&candidates[i]==candidates[i-1])continue;
        sum+=candidates[i];
        path[pathTop++]=candidates[i];
        backtracking(candidates,candidatesSize,target,sum,i+1);
        sum-=candidates[i];;
        pathTop--;
    }
}
int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
    path=(int*)malloc(sizeof(int)*50);
    ans=(int**)malloc(sizeof(int*)*100);
     length=(int*)malloc(sizeof(int)*100);
     ansTop=pathTop=0;
      qsort(candidates, candidatesSize, sizeof(int), cmp);
     backtracking(candidates,candidatesSize,target,0,0);
     *returnSize=ansTop;
    *returnColumnSizes=(int*)malloc(sizeof(int)*ansTop);
     for(int i=0;i<ansTop;i++)
     (*returnColumnSizes)[i]=length[i];
     return ans;
}

39.组合总和

给你一个 无重复元素 的整数数组 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、递归函数参数

和77. 组合一样,依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集。

这里我依然定义path 和 result为全局变量。length存放每个组合的长度。

至于为什么取名为path?从上面树形结构中,可以看出,结果其实就是一条根节点到叶子节点的路径。

接下来还需要如下参数:

target(int)目标和,也就是题目中的n。

candidates(int*)数组

candidatesSize(int)就是题目中数组的大小。

sum(int)为已经收集的元素的总和,也就是path里元素的总和。

startIndex(int)为下一层for循环搜索的起始位置。

2、递归终止条件

终止条件为 sum > target 和 sum == target。

3、单层搜索的逻辑

单层for循环依然是从startIndex开始,搜索candidates集合。

c 复制代码
int* path;
 int pathTop;
 int** ans;
 int ansTop;
 int* length;
void  backtarcking(int* candidates,int candidatesSize,int target,int sum,int startIdx){
    if(sum>=target){
        if(sum==target){
            int* tmp=(int*)malloc(sizeof(int)*pathTop);
            for(int i=0;i<pathTop;i++){
                tmp[i]=path[i];
            }
                length[ansTop]=pathTop;
                ans[ansTop++]=tmp;
            
        }
        return;
    }
    for(int i=startIdx;i<candidatesSize;i++){
        sum+=candidates[i];
        path[pathTop++]=candidates[i];
        backtarcking(candidates,candidatesSize,target,sum,i);
        sum-=candidates[i];
        pathTop--;
    }
}
int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
    path=(int*)malloc(sizeof(int)*50);
    ans=(int**)malloc(sizeof(int*)*200);
    length=(int*)malloc(sizeof(int)*200);
    pathTop=ansTop=0;
    backtarcking(candidates,candidatesSize,target,0,0);
    *returnSize=ansTop;
    *returnColumnSizes=(int*)malloc(sizeof(int)*ansTop);
    for(int i=0;i<ansTop;i++)
    (*returnColumnSizes)[i]=length[i];
    return ans;
}
相关推荐
天若有情673几秒前
【自研实战】轻量级ASCII字符串加密算法:从设计到落地(防查岗神器版)
网络·c++·算法·安全·数据安全·加密
DeeplyMind10 分钟前
第七章:数据结构大比拼
数据结构·计算机科学·少儿编程·少儿科技读物
元亓亓亓12 分钟前
考研408--数据结构--day8--遍历序列&线索二叉树
数据结构·考研·408·线索二叉树
xiaoxue..18 分钟前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试
啊森要自信37 分钟前
CANN ops-cv:AI 硬件端视觉算法推理训练的算子性能调优与实战应用详解
人工智能·算法·cann
仟濹1 小时前
算法打卡day2 (2026-02-07 周五) | 算法: DFS | 3_卡码网99_计数孤岛_DFS
算法·深度优先
驭渊的小故事1 小时前
简单模板笔记
数据结构·笔记·算法
YuTaoShao1 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法一)前后缀分解
算法·leetcode·职场和发展
VT.馒头1 小时前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展
goodluckyaa2 小时前
LCR 006. 两数之和 II - 输入有序数组
算法