# 🔥 数组去重:从双重循环到 Set,面试官想听什么?

🔥 数组去重:从双重循环到 Set,面试官想听什么?

手写 unique 是大厂面试的高频题,但仅仅写出来不够。

本文带你逐层优化 ,分析时间/空间复杂度,写出让面试官眼前一亮的代码。

一、题目与基础要求

js 复制代码
// 输入
[1, 2, 3, 2, 5]
// 输出
[1, 2, 3, 5]

最基本的得分点:

  • 函数命名清晰(unique)
  • 参数校验(Array.isArray)
  • 写注释(函数功能、参数、返回值、作者、日期)

二、方法一:双重循环(O(n²))

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  if (arr.length === 0) return [];
  
  const res = [arr[0]];
  for (let i = 1; i < arr.length; i++) {
    let duplicate = false;
    for (let j = 0; j < res.length; j++) {
      if (arr[i] === res[j]) {
        duplicate = true;
        break;
      }
    }
    if (!duplicate) res.push(arr[i]);
  }
  return res;
}

复杂度 :时间 O(n²) | 空间 O(n)
优点 :兼容性好,思路简单
缺点:大数据量性能差

三、方法二:indexOf + 新数组(O(n²))

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) {
      res.push(arr[i]);
    }
  }
  return res;
}

indexOf 内部也是循环,所以仍然是 O(n²),但代码更简洁。

四、方法三:filter + indexOf(函数式编程)

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

原理 :indexOf 总是返回第一次出现的位置,只有当前下标等于第一次出现位置时才保留。

⚠️ 注意 :NaN 去重会失效(因为 NaN !== NaN),但面试时可以主动提出来,体现你的知识深度。

五、方法四:先排序再去重(O(n log n))

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  const sorted = [...arr].sort();  // 拷贝后排序,不改变原数组
  const res = [sorted[0]];
  for (let i = 1; i < sorted.length; i++) {
    if (sorted[i] !== sorted[i - 1]) {
      res.push(sorted[i]);
    }
  }
  return res;
}

复杂度 :排序 O(n log n) + 遍历 O(n) → O(n log n)
注意:必须拷贝原数组,避免副作用。

六、方法五:对象字面量(空间换时间,O(n))

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  const res = [];
  const seen = {};
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (!seen[item]) {
      res.push(item);
      seen[item] = 1;
    }
  }
  return res;
}

复杂度 :时间 O(n) | 空间 O(n)
缺点 :对象 key 只能是字符串,1 和 '1' 会被视为相同。
改进 :使用 Map(ES6),可以区分类型。

js 复制代码
// 使用 Map 修复类型区分问题
function unique(arr) {
  const map = new Map();
  const res = [];
  for (const item of arr) {
    if (!map.has(item)) {
      map.set(item, true);
      res.push(item);
    }
  }
  return res;
}

七、方法六:Set(最优解,ES6)

js 复制代码
function unique(arr) {
  if (!Array.isArray(arr)) return [];
  return [...new Set(arr)];
}

为什么最强?

  • 时间复杂度 O(n)
  • 代码极简
  • 正确处理 NaN、undefined、null
  • Set 内部基于哈希表,性能稳定

八、面试加分项:时间/空间复杂度对比表

方法 时间复杂度 空间复杂度 是否改变原数组 推荐度
双重循环 O(n²) O(n) 否 ⭐
indexOf O(n²) O(n) 否 ⭐⭐
filter + indexOf O(n²) O(n) 否 ⭐⭐
排序后去重 O(n log n) O(n) 需拷贝避免 ⭐⭐⭐
对象 / Map O(n) O(n) 否 ⭐⭐⭐⭐
Set O(n) O(n) 否 ⭐⭐⭐⭐⭐

九、完整代码(带注释 + 健壮性)

js 复制代码
/**
 * 数组去重(使用 Set)
 * @param {Array} arr - 输入数组
 * @returns {Array} - 去重后的新数组
 * @author kisshyshy
 * @date 2026-05-28
 */
function unique(arr) {
  // 参数校验:必须是数组
  if (!Array.isArray(arr)) {
    console.error('unique 的参数必须是数组');
    return [];
  }
  // 边界处理:空数组直接返回
  if (arr.length === 0) return [];
  
  // 核心:Set 自动去重,再转为数组
  return [...new Set(arr)];
}

// 测试
console.log(unique([1, 2, 3, 2, 5]));        // [1,2,3,5]
console.log(unique([NaN, NaN, 1]));           // [NaN,1]
console.log(unique([]));                      // []
console.log(unique('string'));                // [] + 错误提示

十、总结

大厂面试手写 unique,不要只给一个答案。正确的流程是:

  1. 先写参数校验、边界处理
  2. 给出一种最直观的方法(如双重循环)并分析复杂度
  3. 逐步优化,引出 indexOf、filter、排序法
  4. 最终给出 Set 最优解,并解释为什么它最好
  5. 主动提一下 NaN 去重的问题和 Map 的改进

这样,你展现的不仅是代码能力,更是算法思维、工程素养和沟通能力。


💬 评论区聊聊:你第一次写数组去重用的是哪种方法?踩过哪些坑?

本文作者:kisshyshy
一个热爱分享底层技术与工程实践的 AI 全栈开发者

相关推荐
她说可以呀7 小时前
Vue插槽
前端·javascript·vue.js
qq_448011167 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
Yuzhiyuxia8 小时前
【AI Agent面试系列】AI Agent记忆机制知识点1
人工智能·面试·职场和发展
FRing0310 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
考研保研资料分享11 小时前
2025公卫保研清华专硕:三校考核、英文读译与择校复盘
面试
兰亭妙微UI设计公司12 小时前
兰亭妙微ui设计公司分享:B端组件命名如何支持团队协作
前端·javascript·vue.js
小大宇13 小时前
Vue知识点-ElementPlus-03提示框+询问+通知
前端·javascript·vue.js
前端技术官14 小时前
前端的技术判断力是怎么练出来的?
javascript·web前端·localstorage·debounce·弱网处理
Lichee2015 小时前
招行Java 面试知识点梳理:Spring 事务、锁、线程池、集合
java·面试
程序员大阳15 小时前
副队长JavaScript教程(60)-BOM 初识:window 对象与浏览器模型
javascript·bom·window对象·浏览器模型