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

从数值对象中计算匹配目标值的加法组合

非递归实现多数值求和匹配目标值的方案

当然有不用递归的解决办法!而且不管是2数之和、3数之和,甚至是任意N数之和的场景,都能通过迭代的方式搞定。我给你一步步拆解,结合代码示例来说明:

第一步:预处理数据

首先把你的带键对象转换成便于处理的数组,保留键和数值的结构,这样最后能直接返回对应的键而不只是数值。举个例子,假设你的对象是:

const numObj = { a: 10, b: 20, c: 30, d: 40, e: 50 };

我们可以转成这样的数组:

const numArr = Object.entries(numObj).map(([key, val]) => ({ key, value: Number(val) }));

1. 2数之和的高效非递归实现

你之前用的线性遍历如果是双重循环的话,其实可以优化成哈希表(Map)的方式,时间复杂度从O(n²)降到O(n):

function findTwoSum(target, numArr) {
  const valueMap = new Map();
  for (const item of numArr) {
    const complement = target - item.value;
    if (valueMap.has(complement)) {
      return [valueMap.get(complement).key, item.key];
    }
    valueMap.set(item.value, item);
  }
  return null; // 找不到匹配
}

// 测试:目标50
console.log(findTwoSum(50, numArr)); // 可能返回 ["b", "d"](20+40)或者["a", "e"](10+50)

2. 3数之和的非递归实现

这里用排序+双指针的思路,完全不用递归:

function findThreeSum(target, numArr) {
  // 先排序数组,方便双指针操作
  const sortedArr = [...numArr].sort((a, b) => a.value - b.value);
  const n = sortedArr.length;

  for (let i = 0; i < n - 2; i++) {
    // 跳过重复值(如果有重复数值的话)
    if (i > 0 && sortedArr[i].value === sortedArr[i-1].value) continue;
    
    const remain = target - sortedArr[i].value;
    let left = i + 1;
    let right = n - 1;

    while (left < right) {
      const currentSum = sortedArr[left].value + sortedArr[right].value;
      if (currentSum === remain) {
        return [sortedArr[i].key, sortedArr[left].key, sortedArr[right].key];
      } else if (currentSum < remain) {
        left++;
      } else {
        right--;
      }
    }
  }
  return null;
}

// 测试:目标60
console.log(findThreeSum(60, numArr)); // 比如 ["a", "b", "c"](10+20+30)

3. 通用N数之和的非递归实现(任意数量数值相加)

如果需要支持任意N个数相加的场景,可以用迭代式回溯(用栈模拟递归的调用栈,本质是非递归)。核心思路是逐步构建数值组合,每次添加一个新的数值,直到组合的和等于目标值,或者超过目标值就回溯:

function findNSum(target, numArr, n) {
  if (n < 2 || numArr.length < n) return null;
  
  // 排序,方便剪枝(提前终止不可能的组合)
  const sortedArr = [...numArr].sort((a, b) => a.value - b.value);
  // 用栈存储当前的组合状态:[当前索引, 当前组合, 当前和]
  const stack = [];

  // 初始化栈:每个元素作为初始组合的第一个元素
  for (let i = 0; i < sortedArr.length; i++) {
    stack.push([i, [sortedArr[i]], sortedArr[i].value]);
  }

  while (stack.length > 0) {
    const [currentIndex, currentCombo, currentSum] = stack.pop();
    
    if (currentCombo.length === n) {
      if (currentSum === target) {
        return currentCombo.map(item => item.key);
      }
      continue;
    }

    // 继续添加后续的元素,避免重复组合
    for (let i = currentIndex + 1; i < sortedArr.length; i++) {
      // 剪枝:如果当前和加上剩下的所有元素都不够目标值,直接跳过
      const remaining = n - currentCombo.length;
      const maxPossibleSum = currentSum + sortedArr[i].value * remaining;
      if (maxPossibleSum < target) continue;
      // 剪枝:如果当前和加上当前元素已经超过目标值,后面的更大,直接break
      if (currentSum + sortedArr[i].value > target) break;

      stack.push([
        i,
        [...currentCombo, sortedArr[i]],
        currentSum + sortedArr[i].value
      ]);
    }
  }

  return null;
}

// 测试:目标60,找3个数之和
console.log(findNSum(60, numArr, 3)); // 同样能找到["a", "b", "c"]
// 测试:目标100,找4个数之和(10+20+30+40)
console.log(findNSum(100, numArr, 4)); // 返回["a", "b", "c", "d"]

这些方法都是纯迭代实现,完全不需要递归调用,而且通过排序、剪枝、哈希表等技巧优化了时间复杂度,比单纯的暴力遍历高效很多。如果你的对象里有重复数值,还可以在代码里加上去重逻辑,避免返回重复的组合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:57:55