这道题的解法,刚开始我本身作的并不算是一个很好的解法,只能说题目是作出来了,但过程当中的计算有大量的重复计算,致使函数复杂度直逼O(n^n)
。查阅资料以后便有了一个改良版。感谢这篇文章Find all distinct subset (or subsequence) sums of an array!javascript
最近由于一些缘由,作了几道“简单”的算法题。今天要讲的即是其中的一道题:若是你有一个硬币数组和一个表明其数量的数组,如何获得一共有多少种不一样组合所得的和?java
好比:硬币数组[10, 50, 100]
,和表明其数量的数组[1, 2, 1]
就表示这里一共有三种硬币,1枚10分,2枚50分和1枚100分硬币。那么其可能的排列组合则包括算法
则,不重复的值则包括加黑的部分,一共是9个。数组
而咱们如今的问题即是,输入两个数组:硬币数组和表明其数量的数组,获得一个整数的返回值。函数
根据分析,函数的输入与输出应该规定以下。oop
/** * Count number of * @param {Array<Number>} coins array contains coins with different values * @param {Array<Number>} counts array contains corresponding counts of different coins * @returns {Number} The count of distinct sums */ function countDistinctSum(coins, counts) { // Implementation }
首先,咱们应该先作一个检查,若是coins的长度跟counts的长度并不相等的话,则函数不该该继续处理,而是应该返回一个错误值。暂且定为-1
吧。测试
而后,咱们采用key value pair的形式来储存不一样和(sum)的数量。因此咱们必须有一个名叫result
的对象。固然,其中并无任何的key。在函数运行必要的计算以后,再返回result
的key的数量做为最后的答案。code
另外,咱们还须要将全部硬币组合起来,组成一个新的数组coinList
,其中包含了全部的硬币。好比:硬币数组[10, 50, 100]
,和表明其数量的数组[1, 2, 1]
,组合成[10, 50, 50, 100]
。这一部分用两个for loop轻松搞定。对象
代码以下:递归
function countDistinctSum(coins, counts) { if(coins.length !== counts.length) { return -1; } const results = {}; const coinList = []; for(let i = 0; i < coins.length; i++){ for(let j = 0; j < counts[i]; j++) { coinList.push(coins[i]); } } processList(coinList, coinList.length, 0, 0, results); return Object.keys(results).length - 1; // Remove the empty 0 coin element }
当咱们获得一个coinList以后,接下来咱们便要处理这个数组,将其内部的元素全都排列一遍。好比,[10, 50, 100]
就有如下排列方法。
[10]
[10, 50]
[10, 100]
[10, 50, 100]
[50]
[50, 100]
[100]
这时,我便考虑使用递归法(recursive method)来解决这个问题。
函数接受两个输入
results
用来储存已经有的sumn
用来储存硬币数组的长度sum
用来储存临时已经叠加的和currentIndex
用来记录当前遍历到的索引coinList
用来输入当下数组里的硬币元素。设置出口条件
currentIndex
大于coinList
的长度时,返回。currentIndex
等于coinList
的长度时,调用addToResults
函数(下一步讲解)而后返回。递归函数
processList()
会被递归两次,这二者之间的带入参数区别只有sum
的不一样而已processList()
将带入sum
加上当下的coin值,达到计算累计的效果。好比:[10, 50, 50, 100]
一路加到最后成为210
。processList()
将之带入当下的sum
值去到下一个递归。随着index的增长,该sum
值会将一直被保留直到最终被加入result
,它也将实现跳跃相加的方法。好比:[10, 50, 50, 100]
其中的10 + 100
就能够在这个递归中实现。代码以下:
/** * Recursive function to collect all the sums from all combinations * @param {Array<Number>} coinList array of coins * @param {Number} n the length of coin array * @param {Number} sum the current accumulated value * @param {Number} currentIndex the current processing index in the coin array * @param {Object} results existing result object brought into recursive function for recording sum */ function processList(coinList, n, sum, currentIndex, results) { if(currentIndex > n) { return; } if(currentIndex === n){ addToResults(results, sum); return; } processList(coinList, n, sum + coinList[currentIndex], currentIndex + 1, results); processList(coinList, n, sum, currentIndex + 1, results); }
由于result
自己是空的,当咱们算出一个和是result
里没有的key的话,必须初始化这个key,使其值为1。反之,则只须要在其值上加1即可。
代码以下:
/** * Add the number to results as a key * @param {Object} results * @param {Number} number */ function addToResults(results, number) { if(results[number]) { results[number]++; } else { results[number] = 1; } }
完整代码:
/** * Count number of * @param {Array<Number>} coins array contains coins with different values * @param {Array<Number>} counts array contains corresponding counts of different coins * @returns {Number} The count of distinct sums */ function countDistinctSum(coins, counts) { if(coins.length !== counts.length) { return -1; } const results = {}; const coinList = []; for(let i = 0; i < coins.length; i++){ for(let j = 0; j < counts[i]; j++) { coinList.push(coins[i]); } } processList(coinList, coinList.length, 0, 0, results); return Object.keys(results).length - 1; // Remove the empty 0 coin element } /** * Recursive function to collect all the sums from all combinations * @param {Array<Number>} coinList array of coins * @param {Number} n the length of coin array * @param {Number} sum the current accumulated value * @param {Number} currentIndex the current processing index in the coin array * @param {Object} results existing result object brought into recursive function for recording sum */ function processList(coinList, n, sum, currentIndex, results) { if(currentIndex > n) { return; } if(currentIndex === n){ addToResults(results, sum); return; } processList(coinList, n, sum + coinList[currentIndex], currentIndex + 1, results); processList(coinList, n, sum, currentIndex + 1, results); } /** * Add the number to results as a key * @param {Object} results * @param {Number} number */ function addToResults(results, number) { if(results[number]) { results[number]++; } else { results[number] = 1; } }
测试:
const a = [10, 50, 100]; const b = [1, 2, 1]; console.log('Result:', countDistinctSum(a, b)) // Result: 9