
本文介绍一种基于贪心逻辑的数组分组方法:遍历原数组,持续累加元素到当前组,一旦加入下一个元素会导致总和超过20,则开启新组;支持「严格小于20」和「小于等于20」两种边界策略,并提供可读性强的函数式实现。
本文介绍一种基于贪心逻辑的数组分组方法:遍历原数组,持续累加元素到当前组,一旦加入下一个元素会导致总和超过20,则开启新组;支持「严格小于20」和「小于等于20」两种边界策略,并提供可读性强的函数式实现。
在处理文件批量合并、资源打包或内存分片等场景时,常需将一组带权重(如文件大小,单位 MB)的元素划分为若干子集,使得每个子集的权重总和不超过指定上限(例如 20)。关键约束是:必须保持原始顺序,且采用“尽可能装满”的贪心策略——即从左到右扫描,只要当前组加上下一个数仍满足条件,就继续添加;否则新建一组。
该问题并非经典子集和(NP-hard),而是一个顺序受限的贪心分组问题,无需回溯或递归搜索,但可通过高阶函数优雅实现。下面给出清晰、模块化、可复用的解决方案:
✅ 核心思路:reduce + 动态分组函数
我们利用 Array.prototype.reduce 遍历数组,维护一个二维数组 result(即分组结果),每轮判断是否将当前元素 v 加入最后一组(result.at(-1)):
- 若最后一组为空(即
i === 0)或加入v后仍满足阈值条件 → 追加到末组; - 否则 → 新建一组
[v]。
为支持灵活的边界语义,我们封装两个比较函数:
const lessThan = max => sum => sum sum => sum <p>再定义通用分组生成器 <code>group</code>:</p><pre class="brush:php;toolbar:false;">const add = (a, b) => a + b;
const group = (shouldContinue) => (result, value, index) => {
if (index === 0 || !result.length ||
!shouldContinue(result.at(-1).reduce(add, 0) + value)) {
result.push([value]); // 开启新组
} else {
result.at(-1).push(value); // 追加到当前组
}
return result;
};? 实际使用示例
const data = [3, 8, 9, 2, 7, 5, 6, 5, 3, 11, 9, 17, 6, 5, 8, 4, 2, 7, 9, 12, 5, 16, 4];
// 策略1:每组总和 g.reduce(add, 0)));
// → [20, 18, 19, 20, 19, 20, 19, 20, 19, 20]
console.log('≤20 分组:', resultInclusive.map(g => g.reduce(add, 0)));
// → [20, 18, 19, 20, 19, 20, 19, 20, 19, 20]? 注意:示例中
3 + 8 + 9 = 20被完整保留为一组,符合题设「9 + 2 + 7 = 18 9 + 2 + 7 + 5 = 23 > 20,故5成为下一组起点。
⚠️ 重要注意事项
-
不适用纯递归求解:题目中提供的
sum(array)递归仅计算总和,无法建模“按序分段”逻辑;强行递归会显著降低可读性与性能,且易栈溢出。 -
边界语义明确区分:
lessThan(20)和upTo(20)决定是否接受恰好等于 20 的组合,务必根据业务需求选择(如文件系统块对齐可能要求)。 -
空数组/单元素安全:上述
group函数已通过!result.length判断兼容边缘情况。 - 性能为 O(n):仅一次遍历,无嵌套循环,适合大规模数据(如数百个文件)。
✅ 总结
本方案摒弃复杂递归,转而采用函数式、声明式的 reduce 分组模式,兼顾简洁性、可测试性与业务可配置性。你只需替换 data 和调整 upTo(20) 中的阈值,即可复用于任意数值型顺序分组任务——无论是日志切片、API 批量请求限流,还是前端 chunk 加载优化。










