数组去重不依赖排序,但排序后相邻比较可将时间复杂度从o(n²)降至o(n),前提是接受顺序改变或本需排序;有序数组可用双指针原地去重,o(1)空间;sort不能加速set或indexof。

JS 数组去重本身不依赖排序,但当你把排序作为预处理步骤时,确实能改变去重的逻辑路径和性能表现——关键不在“提升查找”,而在于“规避查找”。真正优化的是判重方式:从逐个比对(O(n²))变成相邻比较(O(n)),前提是允许打乱原顺序或主动接受排序结果。
排序后相邻比较:用 O(n) 替代嵌套查找
对数组先 sort(),再单次遍历只比当前项与前一项是否相等。相同就跳过,不同就保留。整个过程只需一次线性扫描,时间复杂度降为 O(n),但前提是排序已完成(O(n log n) 总体代价)。
- 适合场景:原始顺序不重要,或业务本就需要排序(如展示价格从低到高)
- 注意点:
sort()默认按字符串排序,数字数组必须传比较函数,否则[10, 2, 1]会排成[1, 10, 2] - 示例代码:
const unique = arr.sort((a,b) => a - b).filter((x,i) => i === 0 || x !== arr[i-1]);
双指针原地去重:省空间、保序、仅限有序输入
如果数组**本来就是有序的**(比如后端返回已排序列表),就不必再调 sort()。直接用快慢指针在原数组上操作:慢指针标记唯一元素应放位置,快指针遍历找新值。不新建数组,空间复杂度 O(1),时间 O(n)。
- 典型应用:LeetCode 26 题,常用于高频数据清洗场景
- 不能用于无序数组,否则会漏掉非相邻重复项
- 示例逻辑:
let slow = 0;<br>for (let fast = 1; fast if (arr[fast] !== arr[slow]) arr[++slow] = arr[fast];<br>}<br>arr.length = slow + 1;
排序 + Map/对象辅助:解决对象数组按字段去重
单纯 Set 无法处理对象,但若先按某字段(如 id)排序,再配合 Map 记录已见字段值,就能边遍历边去重,且保持字段顺序一致。相比无序时遍历全量找 findIndex,排序后可提前终止或简化键生成逻辑。
- 适用:用户列表、商品列表等需按主键去重,且希望结果按该字段升序排列
- 优势:Map 查找仍是 O(1),排序让后续逻辑更可控,避免 JSON.stringify 的性能开销和结构限制
- 简写示意:
const map = new Map();<br>arr.sort((a,b) => a.id - b.id)<br> .filter(item => !map.has(item.id) && map.set(item.id, true));
别误用:排序不能加速 Set 或 filter+indexOf
给数组先 sort() 再用 [...new Set(arr)] 或 filter + indexOf,不会提升性能。Set 内部哈希查找不依赖顺序;indexOf 仍要从头扫,排序反而白花 O(n log n) 时间。这种组合只有语义意义(比如想先排再唯一),没有算法收益。
- 真实高性能路径是:基本类型 →
Set;有序数组 → 双指针;对象数组 →Map键提取 - 排序的价值,始终在于它改变了数据分布,从而启用更简单的判重策略,而不是给已有方法“加速”











