You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数组目标和递归求解代码逻辑解析:递归调用与组合清理疑问

组合总和问题代码解析

我能理解这段找数组中和为目标值所有组合代码的基础逻辑,但搞不懂递归怎么找到更多组合,也不清楚递归结束后combination数组是怎么被“清理”的。试过桌面模拟和ChatGPT,还是没完全搞懂整体运行机制,核心困惑在for循环里的递归调用,以及为什么有时候i和start的值不一样。

原代码(注:代码存在一处缺失,正确逻辑需添加push步骤)

function findSums(nums, target) {
    function findSum(target, start, combination) {
        
        if (target === 0) {
            result.push([...combination]);
            return;
        }
        for (let i = start; i < nums.length; i++) {
            console.log('i: ',i,'start: ',start,'n1:',nums[i],'n2:',nums[i-1]);

            if (i > start && nums[i] === nums[i - 1]) {                
                continue;
            }
            // 原代码缺失:combination.push(nums[i]);
            findSum(target - nums[i], i + 1, combination);
            combination.pop();
        }
    }
    nums.sort((a, b) => a - b);
    let start = 0;
    let combination = [];
    let result = [];
    findSum(target, start, combination);
    return result;
}
console.log(findSums([1, 3, 2, 4, 5], 6));

核心逻辑拆解

1. 代码目标:找不重复的元素组合

这段代码的作用是找出数组中元素不重复使用、组合本身也不重复的所有和为目标值的组合。数组先排序是为了方便去重和按顺序遍历。

2. 递归+回溯的运行机制

递归在这里是用来深度遍历所有可能的组合路径,而combination.pop()是回溯的核心清理步骤,两者配合遍历所有合法组合:

递归参数的意义

  • target:当前还需要凑齐的数值
  • start:当前循环从数组的哪个索引开始选元素(避免生成[1,2]和[2,1]这种重复组合)
  • combination:当前正在构建的组合数组(复用同一个数组,节省内存)

for循环与递归的配合

循环从start而非0开始,是为了保证组合元素按排序后的顺序选取,不会回头选之前的元素,从根源避免重复组合。

以排序后的数组[1,2,3,4,5]、目标值6为例,走一遍核心流程:

  1. 第一层循环start=0,i=0,先把1加入combination(原代码缺失的push步骤),递归调用findSum(5, 1, [1])。
  2. 第二层循环start=1,i=1,把2加入combination,递归调用findSum(3, 2, [1,2])。
  3. 第三层循环start=2,i=2,把3加入combination,此时target=3-3=0,满足条件,把[1,2,3]的拷贝存入result,返回。
  4. 回到第三层循环,执行combination.pop(),combination变回[1,2],i递增到3,尝试选4:target=3-4=-1,递归里不满足条件,返回后pop,combination还是[1,2];i再递增到4,选5同理,返回后pop,第三层循环结束。
  5. 回到第二层循环,执行combination.pop(),combination变回[1],i递增到2,尝试选3:target=5-3=2,递归里从start=3选元素,都凑不齐2,返回后pop;i继续递增到3选4、4选5,直到第二层循环结束。
  6. 回到第一层循环,执行combination.pop(),combination变回空数组,i递增到1选2,重复上述流程,最终找到[2,4];i递增到4选5,找到[1,5]。

为什么i会和start不同?

  • 同一层循环中,i从start开始逐步递增,所以随着循环推进,i会大于start,这时候如果遇到和前一个元素相同的数值(nums[i] === nums[i-1]),就会跳过,避免生成重复组合。比如数组有[1,1,2],第一层start=0,i=0处理第一个1后,i=1时i>start且nums[1]和nums[0]相同,直接跳过,避免重复生成[1,2]组合。
  • 递归进入下一层时,start会被设置为i+1,意味着下一层只能从当前元素的下一个位置选元素,保证每个元素只被使用一次。

3. combination的“清理”逻辑:回溯的关键

combination.pop()就是清理操作的核心:每次递归返回后,把当前加入组合的元素移除,让组合数组回到上一层递归调用前的状态,这样就能继续尝试同一层循环的下一个元素。

比如你选了元素A,递归处理完所有包含A的组合后,把A从组合里拿掉,再选元素B,处理所有包含B的组合,以此类推,复用同一个数组完成所有组合的构建,不用每次创建新数组,效率更高。

修正后的完整代码

function findSums(nums, target) {
    function findSum(target, start, combination) {
        if (target === 0) {
            result.push([...combination]);
            return;
        }
        for (let i = start; i < nums.length; i++) {
            if (i > start && nums[i] === nums[i - 1]) {                
                continue;
            }
            combination.push(nums[i]); // 补上缺失的push步骤
            findSum(target - nums[i], i + 1, combination);
            combination.pop();
        }
    }
    nums.sort((a, b) => a - b);
    let result = [];
    findSum(target, 0, []);
    return result;
}
console.log(findSums([1, 3, 2, 4, 5], 6)); // 输出:[[1,2,3],[1,5],[2,4]]

内容的提问来源于stack exchange,提问作者Erick Armijos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 22:32:08