算法二刷复盘:LeetCode 39 组合总和 & 22 括号生成(Java 回溯精讲)

目录

[一、LeetCode 39 组合总和(中等)](#一、LeetCode 39 组合总和(中等))

题目描述

[核心思路:回溯 + 剪枝](#核心思路:回溯 + 剪枝)

[Java 完整实现(含优化)](#Java 完整实现(含优化))

复杂度分析

[二、LeetCode 22 括号生成(中等)](#二、LeetCode 22 括号生成(中等))

题目描述

[核心思路:回溯 + 剪枝](#核心思路:回溯 + 剪枝)

[Java 完整实现](#Java 完整实现)

复杂度分析

三、两道题的回溯模板对比(面试重点)

[四、二刷感悟:回溯题的 "三板斧"](#四、二刷感悟:回溯题的 “三板斧”)


二刷回溯题时,才真正感受到这类题的 "模板化魅力"------ 两道题的核心逻辑都是深度优先搜索(DFS)+ 回溯剪枝,只是剪枝条件和终止条件略有差异。今天就把这两道高频题的复盘整理成博客,从模板、实现到面试考点,一次性讲透。


一、LeetCode 39 组合总和(中等)

题目描述

给定一个无重复元素的数组 candidates 和一个目标数 target,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取,且解集不能包含重复的组合。

核心思路:回溯 + 剪枝

这道题是可重复选组合的经典题,核心逻辑是:

  1. 按顺序选,避免重复 :用 startIndex 控制起始位置,每次递归只从 startIndex 往后选,避免 [2,3][3,2] 这类重复组合。
  2. 可重复选取 :选了当前元素后,下一次递归的 startIndex 不变(还是当前位置),允许再次选同一个元素。
  3. 剪枝优化 :如果当前和已经大于 target,直接返回,不再继续递归。

Java 完整实现(含优化)

java

运行

复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 先排序,方便后续剪枝
        Arrays.sort(candidates);
        backtrack(candidates, target, 0, 0);
        return result;
    }

    /**
     * 回溯函数
     * @param candidates 候选数组
     * @param target 目标和
     * @param sum 当前路径的和
     * @param startIndex 起始索引,避免重复组合
     */
    private void backtrack(int[] candidates, int target, int sum, int startIndex) {
        // 终止条件:当前和等于目标和
        if (sum == target) {
            result.add(new ArrayList<>(path));
            return;
        }

        for (int i = startIndex; i < candidates.length; i++) {
            // 剪枝:如果当前元素加上sum已经超过target,后面的元素(已排序)也会超过,直接break
            if (sum + candidates[i] > target) {
                break;
            }
            // 选当前元素
            path.add(candidates[i]);
            // 递归:可重复选,所以startIndex还是i
            backtrack(candidates, target, sum + candidates[i], i);
            // 回溯:移除当前元素
            path.remove(path.size() - 1);
        }
    }
}

复杂度分析

  • 时间复杂度:O (2ⁿ),最坏情况下每个元素都有选或不选两种状态,加上排序的 O (nlogn),整体为 O (nlogn + 2ⁿ)。
  • 空间复杂度 :O (n),递归栈深度和路径的最大长度,最坏情况下为 target/min(candidates)

二、LeetCode 22 括号生成(中等)

题目描述

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。

核心思路:回溯 + 剪枝

这道题是带条件的回溯,核心是保证括号的有效性:

  1. 终止条件 :左括号和右括号的数量都等于 n,此时的组合是有效的。
  2. 选左括号的条件 :左括号数量小于 n
  3. 选右括号的条件:右括号数量小于左括号数量(保证不会出现右括号比左括号多的无效情况)。
  4. 回溯:选了括号后递归,递归结束再移除括号,尝试其他选择。

Java 完整实现

java

运行

复制代码
import java.util.ArrayList;
import java.util.List;

class Solution {
    List<String> result = new ArrayList<>();
    StringBuilder path = new StringBuilder();

    public List<String> generateParenthesis(int n) {
        backtrack(n, 0, 0);
        return result;
    }

    /**
     * 回溯函数
     * @param n 括号对数
     * @param left 已选左括号数量
     * @param right 已选右括号数量
     */
    private void backtrack(int n, int left, int right) {
        // 终止条件:左右括号都用完了
        if (left == n && right == n) {
            result.add(path.toString());
            return;
        }

        // 剪枝:如果右括号数量大于左括号,组合无效,直接返回
        if (right > left) {
            return;
        }

        // 尝试选左括号
        if (left < n) {
            path.append('(');
            backtrack(n, left + 1, right);
            path.deleteCharAt(path.length() - 1);
        }

        // 尝试选右括号
        if (right < left) {
            path.append(')');
            backtrack(n, left, right + 1);
            path.deleteCharAt(path.length() - 1);
        }
    }
}

复杂度分析

  • 时间复杂度:O (4ⁿ/√n),这是卡特兰数的时间复杂度,代表有效括号组合的数量级。
  • 空间复杂度 :O (n),递归栈深度和路径的最大长度都是 2n,即 O (n)。

三、两道题的回溯模板对比(面试重点)

表格

对比项 39. 组合总和 22. 括号生成
问题类型 可重复选组合 条件限制的序列生成
关键变量 startIndex 控制选的范围,避免重复 left/right 控制括号有效性
选元素的限制 只能从 startIndex 往后选,且可重复 左括号不超过 n,右括号不超过左括号
剪枝优化 排序后,当前元素加和超过 target 就 break 右括号数量超过左括号直接 return
终止条件 和等于 target 左右括号都用完

四、二刷感悟:回溯题的 "三板斧"

二刷时发现,回溯题其实都有固定的解题模板,掌握这三步就能应对大部分中等题:

  1. 确定终止条件:什么时候可以把当前路径加入结果集?
  2. 确定选 / 不选的条件:什么情况下可以选当前元素?选了之后需要更新哪些变量?
  3. 回溯恢复现场:递归结束后,要把路径、变量恢复到选之前的状态,避免影响后续选择。
相关推荐
WL_Aurora2 小时前
每日一题——自然倍树
数据结构·python·算法·深度优先
Y学院2 小时前
Spring AI Alibaba 高质量实战教程(从入门到企业级落地)
java·人工智能·spring·自然语言处理
水木流年追梦2 小时前
CodeTop Top 300 热门题目3-字符串相加
java·前端·算法
编程之升级打怪2 小时前
自定义实现Java的HashMap集合
java·开发语言
后端AI实验室2 小时前
我带的那个实习生,比我更依赖AI——但他的问题和我完全不同
java·ai
一江寒逸2 小时前
数据结构与算法之美:绪论——构建算法思维的基石
数据结构·算法
y小花2 小时前
安卓StorageManagerService
android·java
码王吴彦祖2 小时前
AI 逆向分析国航 AirChina FECU 参数来源并实现离线生成
android·java·javascript
LJianK12 小时前
进程、线程、多线程、异步
java·开发语言·jvm