
本文详解 javascript 中统计运动成绩中“最高分新纪录”与“最低分新纪录”次数的正确实现方法,重点解决因数组引用导致的逻辑错误,并提供简洁、高效、可复用的解决方案。
本文详解 javascript 中统计运动成绩中“最高分新纪录”与“最低分新纪录”次数的正确实现方法,重点解决因数组引用导致的逻辑错误,并提供简洁、高效、可复用的解决方案。
在解决“Breaking Records”这类经典算法题时,核心目标是:给定一个整数数组(代表连续比赛得分),统计在整个序列中,最高分被刷新的次数(不包含首项)和最低分被刷新的次数(不包含首项)。注意:仅当出现严格大于当前最高分或严格小于当前最低分时,才视为一次“破纪录”。
原始代码逻辑方向基本正确——分别用 HP(High Peak)和 LP(Low Peak)函数模拟遍历过程并计数。但关键缺陷在于:
✅ var b = array 和 var a = array 实际上只是创建了对原数组的引用副本,而非独立副本;
❌ 因此 HP 函数中对 b.splice() 的修改会直接污染原数组 array,导致后续调用 LP(array) 时传入的已是被 HP 修改过的数组,造成结果错乱。
例如,输入 [10, 5, 20, 20, 4, 5, 2, 25, 1]:
- 初始 temp = 10(最高基准),temp1 = 10(最低基准);
- HP 执行中会删掉所有 ≤10 的后续元素(如 5, 4, 5, 2, 1),最终 b 变为 [10, 20, 25];
- 但此时原 array 已被 splice 改写(因引用共享),LP 接收的是残缺数组,自然无法正确统计低点。
✅ 正确做法是:在每个子函数内使用浅拷贝创建独立数组,如 [...array] 或 array.slice()。这确保两次遍历互不干扰。
以下是优化后的完整实现(已修复、简化并增强可读性):
function breakingRecords(scores) {
if (!scores || scores.length < 2) return [0, 0];
let maxSoFar = scores[0];
let minSoFar = scores[0];
let highBreaks = 0;
let lowBreaks = 0;
for (let i = 1; i < scores.length; i++) {
const score = scores[i];
if (score > maxSoFar) {
maxSoFar = score;
highBreaks++;
}
if (score < minSoFar) {
minSoFar = score;
lowBreaks++;
}
}
return [highBreaks, lowBreaks]; // 注意:题目要求输出格式为 [高破纪录数, 低破纪录数]
}
// 测试
const scores = [10, 5, 20, 20, 4, 5, 2, 25, 1];
console.log(breakingRecords(scores)); // 输出: [2, 4]? 关键改进说明:
- ✅ 单次遍历 O(n):无需两个独立函数、无需 splice 等高开销操作,空间复杂度 O(1),时间效率最优;
- ✅ 语义清晰:maxSoFar / minSoFar 实时跟踪历史极值,> 和
- ✅ 边界安全:开头校验空/短数组,避免运行时错误;
- ✅ 符合题意输出顺序:返回 [highBreaks, lowBreaks],对应样例输出 2 4(即 2 次新高:20→25;4 次新低:5→4→2→1)。
⚠️ 注意事项:
- 原始代码中 count 和 count1 是闭包变量,但被两个函数共享,易引发状态污染——新实现彻底规避该风险;
- filter(...indexOf) 去重逻辑在此题中完全多余(破纪录只看是否超越历史极值,与重复值无关),删除后更精准;
- 避免在循环中修改正在遍历的数组(如 splice + i--),既易出错又影响性能。
总结:算法题不仅要“能跑通”,更要关注数据隔离性、时间复杂度与逻辑健壮性。一个简单的展开运算符 [...array] 或一次重构为单遍历,就能将混乱的多引用逻辑,转化为清晰、可靠、工业级可用的解决方案。

















