首页 > 其他分享 >LeetCode 216. 组合总和 III 回溯写法详解

LeetCode 216. 组合总和 III 回溯写法详解

时间:2024-08-11 21:51:55浏览次数:18  
标签:216 剪枝 题目 组合 int 回溯 path III LeetCode

216. 组合总和 III

216. 组合总和 III

题目来源

216. 组合总和 III

题目分析

题目要求找出所有相加之和为 nk 个数的组合。这些组合只能包含 19 的正整数,并且每个组合中的数字不能重复出现。解集也不能包含重复的组合。

题目难度

  • 难度:中等

题目标签

  • 标签:数组, 回溯

题目限制

  • 1 <= k <= 9
  • 1 <= n <= 60

解题思路

这个问题可以使用回溯算法来解决。我们需要从 19 中选择 k 个数,使得这 k 个数的和为 n。每个数只能选择一次,并且组合中不能包含重复的数字。
其实这题是77.组合多加了一个返回的组合的和要等于n的条件罢了,可以把77.组合的回溯代码改一改,加个判断和是否是n的限定条件就可以了,但是这题因为多了个限制条件,可以根据这个条件使用更多的方法剪枝。

核心算法步骤

  • 回溯算法
    1. 1 - 9 选择数字,从大到小枚举(从小到大也行,但是剩余元素的和就不好写),由于不能重复,人为规定,每次选择的数字要是递减的,每次选择一个数字后,将 选择的数字j加入路径path 末尾,同时将目标和 t 减去该数字,然后向下递归枚举。
    2. 边界条件:当 d0 时,无需判断当前组合的和是否为 n,因为使用目标和t条件的剪枝之后,能选完的组合和肯定是n,将该组合加入到结果集中。
    3. 通过剪枝可以提高效率,避免不必要的递归。
      1. 如果剩余元素小于要选择的的个数d,剪枝
      2. t为剩下要凑的和,如果路径path中的和已经大于t,已经凑多了,则剪枝,或者剩下的最大d个数的和(即ii-d+1等差数列求和)小于t,不可能凑齐了则剪枝。

代码实现

以下是生成符合条件的组合的Java代码:

/**
 * 216. 组合总和 III
 * @param k 输入的整数k
 * @param n 输入的整数n
 * @return ans 所有可能的组合
 */
public List<List<Integer>> combinationSum3(int k, int n) {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();//组合的可能路径
    if (k > n) {
        return ans;
    }
    combinationSum3(k, 9, n, path, ans);
    return ans;
}

private void combinationSum3(int k, int i, int t, List<Integer> path, List<List<Integer>> ans) {
    int d = k - path.size(); // 还需选择d个数
    // 剪枝条件,如果剩余数t小于0或者大于剩余d个数的最大和,则剪枝
    if (t < 0 || t > ((2 * i - d + 1) * d / 2)) {
        return;
    }
    if (d == 0) {
        ans.add(new ArrayList<>(path));
        return;
    }
    for (int j = i; j >= d; j--) {
        path.add(j);
        combinationSum3(k, j - 1, t - j, path, ans);
        path.remove(path.size() - 1);
    }
}

代码解读

  • 回溯逻辑

    • 我们从 9 开始向下枚举数字,每次选择一个数字后,递归处理剩下的 j-1 个数字,并更新剩余的目标和 t
    • 当剩余的 d0 时,无需检查当前路径上的和是否为目标值 n,将该路径添加到结果集中。
  • 剪枝逻辑

    • 剪枝的条件基于剩余的目标和 tk。如果剩余的目标和 t 小于 0,或者剩余的 d 个最大数的和小于 t,则当前路径不可能满足条件,直接返回。
    • 通过计算等差数列和,可以快速判断是否继续递归。

性能分析

  • 时间复杂度O(k*C(9, k)),回溯算法会遍历所有可能的组合。
  • 空间复杂度O(k),递归调用栈的最大深度为 k

测试用例

你可以使用以下测试用例来验证代码的正确性:

int k = 3, n = 7;
List<List<Integer>> result = combinationSum3(k, n);
System.out.println(result);
// 输出: [[1, 2, 4]]

int k = 3, n = 9;
List<List<Integer>> result2 = combinationSum3(k, n);
System.out.println(result2);
// 输出: [[1, 2, 6], [1, 3, 5], [2, 3, 4]]

扩展讨论

优化写法

通过预先判断组合是否有可能达到目标值 n,可以减少递归的次数,使算法更加高效。

其他实现

除了回溯法,还可以通过动态规划或广度优先搜索来求解组合问题,但这些方法可能不如回溯法直观和高效。

总结

这道题目通过回溯算法帮助我们理解了如何从一组数字中生成满足条件的组合。通过剪枝,我们能够有效减少不必要的计算,提升算法的性能。这种思路在解决类似的组合数学问题时非常有效。


标签:216,剪枝,题目,组合,int,回溯,path,III,LeetCode
From: https://blog.csdn.net/Lentr0py/article/details/141111963

相关文章

  • Leetcode 热题100 - 155 最小栈
    Leetcode热题100-155最小栈1.题目描述2.解题思路3.代码实现(方法二)4.c++知识点用法1.题目描述155最小栈2.解题思路方法一:创建一个辅助栈min_stk用以存储当前元素相对应的栈中最小元素值;方式二:类似于方法一,使用pair<int,int>同时存储当前元素与其对应......
  • Leetcode-3129 找出所有稳定的二进制数组I
    Leetcode-3129找出所有稳定的二进制数组I1.题目描述2.解题思路3.代码实现1.题目描述3129找出所有稳定的二进制数组I2.解题思路(1)定义f[i][j][k]表示i个0、j个1且当前位i+j填写值为k=0/1的所有情况;(2)对于f[i][0][0]、f[0][j][1]初始化为1,注意到:......
  • Leetcode-3132 找出与数组相加的整数II
    Leetcode-3132找出与数组相加的整数II1.题目描述2.解题思路3.代码实现1.题目描述3132找出与数组相加的整数II2.解题思路(1)排序后,注意到nums1数组比nums2数组多两个元素,可推出最小匹配元素一定在nums[0]、nums[1]、nums[2]中出现;(2)优先从nums[2]进行判......
  • 「LeetCode Top100」之滑动窗口
    3.无重复字符的最长子串题目链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envType=study-plan-v2&envId=top-100-liked题目难度:中等标签:哈希表、字符串、滑动窗口题目状态:学习题解思路:滑动窗口的思路,也就是维持一个无......
  • LeetCode 算法:最小栈 c++
    原题链接......
  • 「LeetCode Top100」之双指针
    283.移动零题目链接:https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=top-100-liked题目难度:简单标签:数组、双指针题目状态:AC思路:两个指针,i用来找0,j用来找非0。当nums[i]==0&&nums[j]!=0时,将两者交换。代码:classSolutio......
  • Leetcode 206. 反转链表
    给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。示例1:输入:head=[1,2,3,4,5]输出:[5,4,3,2,1]示例2:输入:head=[1,2]输出:[2,1]示例3:输入:head=[]输出:[]提示: 链表中节点的数目范围是 [0,5000]-5000<=Node.val<=5000方法一: //双指......
  • LeetCode | 20 ValidParentheses
    分析括号成对出现,键值对类型括号字符序列嵌套出现,不能错位,顺序具有对称性为什么不用数组这种数据结构来记录数量?因为这种方法不能保证括号的正确顺序。例如,字符串'({[)}]'会被认为是有效的。栈解决有效括号问题当遇到一个左括号时,我们需要记住它,以便在后续遇到相应的右括......
  • LeetCode | 225 Implement Stack Using Queues
    分析阻塞(Blocking)阻塞操作指的是在调用一个函数或方法时,如果该操作不能立即完成(例如,因为需要等待某个事件的发生,如数据达到或资源可用),那么当前线程或进程会被挂起(暂停执行),直到操作完成为止。在这个等待期间,线程或进程无法执行其他任务。等待:调用方必须等待操作完成独......
  • P2168 [NOI2015] 荷马史诗
    题意给定一个字符串\(s\)和整数\(k\)。求:1.k叉哈夫曼树的带权路径之和;2.求合法的哈夫曼树中,最小的高度是多少。思路按照普通二叉哈夫曼树对其进行编码,将其转化为\(k\)叉哈夫曼树。编码有可能出现合并到根节点的时候不足\(k\)个结点,这会造成结果不优,所以我们可以补......