
本文详解如何通过将数组替换为 set 数据结构,将时间复杂度从 o(n²) 降至 o(n),大幅提升如“存在重复元素”类问题的执行效率,实测运行时间可从 6205ms 优化至约 86ms。
本文详解如何通过将数组替换为 set 数据结构,将时间复杂度从 o(n²) 降至 o(n),大幅提升如“存在重复元素”类问题的执行效率,实测运行时间可从 6205ms 优化至约 86ms。
在 LeetCode 第 217 题《存在重复元素》(Contains Duplicate)中,一个常见但低效的解法是使用普通数组 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() 在每次调用时需遍历整个 dupes 数组,导致内层操作平均时间复杂度为 O(n),整体达 O(n²)。当输入规模为 10⁵ 量级时,最坏情况比较次数超 10¹⁰ 次,直接造成 6205ms 的超长耗时。
✅ 正确优化路径是改用原生 Set —— 它基于哈希表实现,add()、has() 和 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;? 关键改进点:
立即学习“Java免费学习笔记(深入)”;
- 用 Set 替代 Array,消除线性查找开销;
- 使用 for...of 提升可读性与现代语法兼容性;
- 提前返回(early return):一旦发现重复立即返回 true,避免冗余遍历;
- seen.size 是常量时间访问,无需 dupes.length 的数组长度计算(虽也是 O(1),但语义更精准)。
⚠️ 注意事项:
- Set 对 NaN 和 -0 有特殊行为(new Set([NaN, NaN]) 大小为 1;-0 === +0 为 true,Set 中视为同一值),但本题约束为整数数组,无需额外处理;
- 不要误用 Set 的构造函数一次性传入数组(如 new Set(nums))再比较大小——这仍会完整遍历,且无法提前终止,失去最优解优势;
- 若需兼容 IE,可用 Map 模拟(键为数字,值为 true),但现代环境强烈推荐原生 Set。
总结:JavaScript 性能优化的核心往往不在“写得更炫”,而在选择合适的数据结构。面对成员存在性查询(contains?)、去重、唯一性校验等场景,Set 应为默认首选;而滥用 Array.includes() 是高频性能陷阱。一次数据结构升级,即可带来近 70 倍的运行时提升——这才是工程实践中最具性价比的优化。











