当前位置: 首页 > article >正文

leetcode——组合总和(回溯算法详细讲解)

在面试或刷题过程中,回溯算法是一个绕不开的核心算法之一。今天,我们来详细解析 LeetCode 39「组合总和」 问题,并用 Java 回溯 + 剪枝优化 来高效解决它!这篇文章不仅适合初学者,也适合希望提高回溯算法的朋友们。

给你一个 无重复元素 的整数数组 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.经过分析,我们可以使用回溯中的选或者不选方法来进行解题。

2.首先,到达边界条件时,且我们的left为零的时候,也就是我们达到了目标值,将列表path添加进ans中。

3.如果到达了边界,且left不为零时,或者left小于零时,直接返回即可。

4.不选,直接进入下一步的递归。

5.选,直接将当前元素添加进path中,然后进入下一步的递归即可。

6.最后,我们要将path最后一个元素删除恢复现场,达到回溯的效果。

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(0, target, candidates, ans, path);
        return ans;
    }
    private void dfs(int i, int left, int[] candidates, List<List<Integer>> ans, List<Integer> path) {
        if (left == 0) {
            ans.add(new ArrayList<>(path));
            return;
        }
        if (i == candidates.length || left < 0) {
            return;
        }
        dfs(i + 1, left, candidates, ans, path);
        path.add(candidates[i]);
        dfs(i, left - candidates[i], candidates, ans, path);
        path.remove(path.size() - 1);
    }
}

时间复杂度分析

时间复杂度分析:
由于回溯算法会遍历所有可能的组合,最坏情况下的时间复杂度大约为 O(2^N),但由于剪枝优化,实际运行效率要高于暴力搜索。
空间复杂度: 主要由递归栈深度决定,为 O(target / min(candidates))

 总结与延伸

 思考:如果 candidates 里有重复元素,该如何去重?
扩展:这道题和「零钱兑换」问题(动态规划解法)有何不同?


http://www.kler.cn/a/535224.html

相关文章:

  • 10. 神经网络(二.多层神经网络模型)
  • Java 23新特性
  • 2023年java面试问题大全及答案大全
  • 图论常见算法
  • 2025最新软件测试面试大全(附答案+文档)
  • C#面试常考随笔13: 泛型的主要约束和次要约束是什么?
  • DNN(深度神经网络)近似 Lyapunov 函数
  • 解锁反序列化漏洞:从原理到防护的安全指南
  • 【OpenCV插值算法比较】
  • 给排水 笔记
  • MapReduce是什么?
  • Swan 表达式 - 数组相关操作
  • 【Prometheus】如何通过golang生成prometheus格式数据
  • 使用 MMCM 的 I/O 时序 ZHOLD/BUF_IN 补偿
  • Spring Boot 入门 与 无法解析符号 springframework 的解决
  • 71.StackPanel黑白棋盘 WPF例子 C#例子
  • 基于Redis分布式锁
  • 达梦数据库从单主模式转换为主备模式
  • (苍穹外卖)项目结构
  • 深度学习|表示学习|卷积神经网络|DeconvNet是什么?|18
  • Android studio 编译速度增加
  • 微服务中服务治理都包含什么
  • 【回溯+剪枝】单词搜索,你能用递归解决吗?
  • [原创](Modern C++)现代C++的关键性概念: 文件编码细节之一:BOM(Byte Order Mark, 字节顺序标记)
  • 分库分表详解
  • 02.06 网络编程_概述