
本文详解如何通过将数组去重逻辑从 array.includes() 迁移至 set.has(),将时间复杂度从 o(n²) 优化至 o(n),实测 leetcode「存在重复元素」题 runtime 从 6205ms 降至约 86ms。
本文详解如何通过将数组去重逻辑从 array.includes() 迁移至 set.has(),将时间复杂度从 o(n²) 优化至 o(n),实测 leetcode「存在重复元素」题 runtime 从 6205ms 降至约 86ms。
在 JavaScript 算法实践中,数据结构的选择往往比逻辑本身更直接影响运行时性能。以 LeetCode 第 217 题「存在重复元素」为例,原始解法使用数组 dupes 存储已见元素,并依赖 Array.prototype.includes() 判断是否存在:
let dupes = [];
for (let i = 0; i < nums.length; i++) {
if (!dupes.includes(nums[i])) {
dupes.push(nums[i]);
}
}
return !(dupes.length === nums.length);该写法看似直观,但存在严重性能瓶颈:includes() 在数组上是线性查找(O(n)),外层循环为 O(n),整体时间复杂度达 O(n²)。当输入规模达 10⁵ 量级时,最坏情况需执行约 10¹⁰ 次比较——这正是 runtime 高达 6205ms 的根本原因。
✅ 正确优化路径是用 Set 替代数组作为查重容器。Set 基于哈希表实现,has()、add() 和 size 均为 O(1) 平均时间复杂度,使整体算法降为 O(n):
const seen = new Set();
for (const num of nums) {
if (seen.has(num)) {
return true; // 提前终止,无需遍历全部
}
seen.add(num);
}
return false;? 进阶提示:上述代码还加入了提前返回优化——一旦发现重复即刻返回 true,避免无谓遍历。相比原版必须构建完整去重集合再比较长度,进一步减少平均执行步数。
立即学习“Java免费学习笔记(深入)”;
⚠️ 注意事项:
- 不要误用 Set 的 length 属性(不存在),应使用 size;
- Set.add() 总是返回 Set 本身,无需检查返回值;
- 若需兼容 IE,可使用 Map 模拟(键为数字/字符串,值任意),但现代环境推荐原生 Set;
- 对于超大数据集,还可考虑 new Set(nums).size !== nums.length 的一行解法(简洁但空间略高,且无法提前终止)。
总结:JavaScript 运行时优化的核心在于 匹配操作语义与数据结构特性。includes() 适合小数组或少量查找;而涉及高频成员判断(如去重、查重、交集)时,Set 是不可替代的首选。一次数据结构升级,即可带来百倍级性能跃升。











