这是一个幼稚的实现,但它非常简单。我们只需找到所有连续(非空)子数组,然后过滤以找到总和与我们的目标匹配的那些:
const sum = (ns) => ns.reduce((a, b) => a + b, 0)
const suffixes = (xs) => xs .map ((_, i) => xs .slice (i))
const prefixes = (xs) => xs .map ((_, i) => xs .slice (0, i + 1))
const subArrays = (xs) => suffixes (xs) .flatMap (prefixes)
const subArraysWithTotal = (ns, t) => subArrays (ns) .filter ((s) => sum (s) == t)
console .log (
subArraysWithTotal ([1, 2, 4, 7], 7)
)
sum 很明显,只需将数组中的数字相加即可。
prefixes 和 suffixes 相似,举个例子应该清楚:
prefixes (['w', 'x', 'y', 'z'])
//=> [['w'], ['w', 'x'], ['w', 'x', 'y'], ['w', 'x', 'y', 'z']]
suffixes (['w', 'x', 'y', 'z'])
//=> [['w', 'x', 'y', 'z'], ['x', 'y', 'z'], ['y', 'z'], ['z']]
subArrays 结合这些,返回每个后缀的所有前缀,这为我们提供了原始的所有连续子数组:
subArrays (['w', 'x', 'y', 'z'])
//=> [['w'], ['w', 'x'], ['w', 'x', 'y'], ['w', 'x', 'y', 'z'],
// ['x'], ['x', 'y'], ['x', 'y', 'z'], ['y'], ['y', 'z'], ['z']]
我们的主要函数 subArraysWithTotal 只是找到所有这些子数组,然后通过计算它们的总和来过滤它们,并将其与我们的目标进行比较。
正如我所说,这很幼稚。它将是空间上的O(n^2) 和时间上的O(n^3)。如果我们知道我们所有的数字都是正数,那么某种滑动窗口技术肯定会提高速度和内存。由于负数的可能性,仍然可能有一个更便宜的算法,但它并不那么明显。
但是我们可以使用生成器函数来减少内存。将上述所有内容(sum 除外)替换为生成器并为生成器添加 filter 函数的替代版本可能如下所示:
const sum = (ns) => ns.reduce((a, b) => a + b, 0)
function* suffixes (xs) {for (let i of xs .keys ()) yield xs .slice (i)}
function* prefixes (xs) {for (let i of xs .keys ()) yield xs .slice (0, i + 1)}
function* subs (xs) {for (let suff of suffixes (xs)) yield* prefixes (suff)}
function* filter (fn, it) {for (let x of it) {if (fn (x)) yield x}}
function* subArraysWithTotal (ns, t) {yield * filter ((s) => sum (s) == t, subs (ns))}
console .log (
[...subArraysWithTotal ([1, 2, 4, 7], 7)]
)
现在我们只需要足够的内存来保存当前子数组。