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

news/2024/7/7 20:04:26 标签: 算法, 数据结构

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收集当前的结果。
所以 终止代码如下:

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
在这里插入图片描述

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

代码

未剪枝

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;
}

剪枝

在这里插入图片描述

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的操作。

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集合。

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;
}

http://www.niftyadmin.cn/n/5535127.html

相关文章

vue3中使用Antv G6渲染树形结构并支持节点增删改

写在前面 在一些管理系统中&#xff0c;会对组织架构、级联数据等做一些管理&#xff0c;你会怎么实现呢&#xff1f;在经过调研很多插件之后决定使用 Antv G6 实现&#xff0c;文档也比较清晰&#xff0c;看看怎么实现吧&#xff0c;先来看看效果图。点击在线体验 实现的功能…

Spring Cloud Alibaba之负载均衡组件Ribbon

一、什么是负载均衡&#xff1f; &#xff08;1&#xff09;概念&#xff1a; 在基于微服务架构开发的系统里&#xff0c;为了能够提升系统应对高并发的能力&#xff0c;开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中&#xff0c;并把访问业务功能的请求均…

如何通过IP地址查询地理位置及运营商信息

在数字时代&#xff0c;IP地址&#xff08;Internet Protocol Address&#xff0c;互联网协议地址&#xff09;已经成为我们日常网络活动的重要组成部分。每台连接到互联网的设备都被分配了一个唯一的IP地址&#xff0c;它不仅可以识别设备&#xff0c;还可以揭示设备的地理位置…

Perl 语言开发(三):运算符和表达式

目录 1. 算术运算符 1.1 基本算术运算符 1.2 自增和自减运算符 2. 字符串运算符 2.1 连接运算符 2.2 重复运算符 3. 赋值运算符 3.1 简单赋值运算符 3.2 复合赋值运算符 4. 比较运算符 4.1 数字比较运算符 4.2 字符串比较运算符 5. 逻辑运算符 5.1 逻辑运算符 5…

HQ-SAM

不建议复现

Liunx网络配置

文章目录 一、查看网络配置永久修改网卡临时修改网卡 二、查看主机名称 hostname三、查看路由表条目 route四、查看网络连接情况netstat五、获取socket统计信息ss六、查看当前系统中打开的文件和进程的工具lsof七、测试网络连通性ping八、跟踪数据包 traceroute九、域名解析 ns…

基于Tools体验NLP编程的魅力

大模型能理解自然语言&#xff0c;从而能解决问题&#xff0c;但是就像人类大脑一样&#xff0c;大脑只能发送指令&#xff0c;实际行动得靠四肢&#xff0c;所以LangChain4j提供的Tools机制就是大模型的四肢。 大模型的不足 大模型在解决问题时&#xff0c;是基于互联网上很…

easyexcel 模板填充Excel数据,实现自定义换行及动态调整行高,并保持列表格式一致

pom依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.5</version> </dependency><dependency><groupId>com.alibaba</groupId><artifa…