
本文介绍一种基于函数式编程思想的高效分组算法:遍历数组,动态累积求和,当加入下一个数会导致总和超过20时,开启新组;支持严格小于20(
本文介绍一种基于函数式编程思想的高效分组算法:遍历数组,动态累积求和,当加入下一个数会导致总和超过20时,开启新组;支持严格小于20(
在文件合并、资源打包或批量任务调度等实际场景中,常需将一组带权重(如文件大小,单位 MB)的元素划分为若干子集,使得每个子集的总权重不超过指定阈值(例如 20)。本问题本质是贪心式滑动分组(Greedy Grouping),而非经典 NP 难的子集和问题——我们不回溯、不穷举,而是按原始顺序逐项尝试累加,一旦超限即切分,确保逻辑可预测、时间复杂度为 O(n)。
以下是核心实现(ES6+,纯函数式风格):
const add = (a, b) => a + b;
const group = (compare) => (result, value, index) => {
// 若是首个元素,或当前组加新值后不满足条件,则新建一组
if (index === 0 || !compare(result.at(-1).reduce(add, 0) + value)) {
result.push([value]);
} else {
// 否则追加到最后一组
result.at(-1).push(value);
}
return result;
};
// 定义两种边界判断函数
const upTo20 = (sum) => sum sum <p>✅ <strong>关键设计说明</strong>:</p>
-
group(compare)是高阶函数,接收一个比较谓词(如sum ),返回符合该约束的分组 reducer; - 使用
Array.prototype.at(-1)安全获取最后一组(兼容空数组初始状态); -
reduce天然适合顺序累积处理,避免手动索引与递归调用,更简洁、不易出错; - 原始递归求和函数
sum(array)仅计算总和,不解决分组逻辑——本方案直接在遍历中维护分组状态,效率更高。
⚠️ 注意事项:
- 此算法依赖输入顺序,不重排数组;若需最优分组(最少分组数),需使用动态规划或装箱算法(如 First-Fit Decreasing),但复杂度显著上升;
- 对于空数组或含非数字元素的情况,建议前置校验:
data.every(Number.isFinite); - 在大型数组中,
reduce性能优异,但若需中断处理(如遇到异常值立即退出),可改用for...of循环增强控制力。
总结:面对“顺序累加、阈值截断”类需求,优先采用 reduce + 谓词函数的组合模式——逻辑清晰、可读性强、易于扩展(如更换阈值、调整比较逻辑),是现代 JavaScript 工程实践中的推荐解法。










