从给定的数组中找到和为M的组合

190 阅读1分钟

前言

我们经常会看到这种需求,从给定的物品列表中找到总价正好是100元的组合。每种组合的元素个数不固定,那么就不能使用暴力遍历的方式了。 举个例子:

从[1, 3, 2, 5, 4, 6] 中找到和为10的组合

我们试图找一下

对顺序没有要求的话,可以找到如下几种组合:[1, 3, 6]、[1, 3, 2, 4]、[1, 4, 5]、[3, 2, 5]、[4, 6],这5种组合包含2、3、4个元素。

分析

可不可以使用递归来做呢? 我们分析一下,对于一个元素A,每一种组合包含两种情况,即组合包含A和组合不包含A。

对于包含A的情况,如果去掉A,那么问题就变成了从剩下的数组中找到和为M - A的组合。这样可以使用递归。

对于不包含A的情况,如果去掉A,那么对结果没有影响,这样依旧可以使用递归。

代码实现

经过分析,我们可以使用如下方法:

let a = []

function findNumber(sum, numberArray, orderIndex) {
  if (orderIndex < 0 || sum <= 0) {
    return
  }
  if (sum === numberArray[orderIndex]) {
    a.push(sum)
    console.log(a.join('-'))
    a.pop()
  }
  a.push(numberArray[orderIndex])
  findNumber(sum - numberArray[orderIndex], numberArray, orderIndex - 1)
  a.pop()
  findNumber(sum, numberArray, orderIndex - 1)
}

/// 测试用例 

const b = [1, 3, 2, 5, 4, 6]

findNumber(10, b, b.length - 1)

// 6-4
// 6-3-1
// 4-5-1
// 4-2-3-1
// 5-2-3

总结

使用递归实现,代码够整洁。但是这个递归比较特殊,所有的结果都引用了a这个变量。在使用某个组合的时候不要修改a数组。当然还有其他的方式,欢迎讨论。