经典面试题:数组去重

101 阅读4分钟

前言

当我们提及数组去重时,想必我们会想,数组去重这不是很简单的算法,为什么会作为频繁出现作为的大厂面试题来对我们考察,其实不然,当我们认为数组去重是很简单的方法时,那想必我们一定是被平常很简单的数组去重的题目冲昏头脑,没有深度去思考数组去重。

数组去重浅显使用方法

let arr = [1,2,3,2,2]

我们来看当我们遇到这种数组内部放的都是原始类型的数据当我们要对其去重那想必我们大家都能实现。

  1. 我们可以通过 set 数据结构的特性把这个数组放入 set 数据类型中实现对数组 arr 的去重操作
  2. 我们不去使用 set 数据结构的话,我们可以创建一个新的数组,遍历原数组将原数组的内容与新数组的内容进行比较如果新数组中没有这个内容那将其放入到新数组中来实现对数组的去重

数组去重深度使用方法

上面这些对数组去重的方式也就是我们日常刷到算法题所使用的一些方式,其实这仅仅适用于数组中放入的时原始类型数据,试想当出现

let arr = [     
{name: '张三', age: 18, like: {n: 1}},     
{name: '李四', age: 19},     
{name: '王五', age: 20},    
{name: '张三', age: 18, like: {n: 1}},   
]

当数组中存放的上面粗放的一个对象时那上面两种方法就不能使用了,我们学习js知道在arr 数组中存放对象引用类型,实际上放的是他的引用地址,那我们使用set 数据类型结构,包括数组遍历比较下,实际上相同的数据内容但他们的引用地址不同,在 V8 中会将他们视作不是重复内容,但在我们实际开发中则将会把他们当作是相同的内容,那我们如何实现对这样的数组进行去重呢?

那我们自己来手搓一个方法来实现不论数组内放的是什么数据类型都能实现去重

function unique(arr){
let res = []
for(let i = 0 ; i<arr.length ; i++){
    if(!res.includes(arr[i])){
        res.push(arr[i])
    }else{
        continue
    }
}
return res
}

我们来看上面的写法,根据数组去重的原理实现,先创建一个新的数组将arr内容与res内容进行判断如果没有则push进入新的数组中,显然这么写仍然不能处理数据内容是引用类型方法。那我们该如何更改呢?

function unique(arr) {
    let res = []
    
    for (let i = 0; i < arr.length; i++) {
      let isFind = false
      for (let j = 0; j < res.length; j++) {
        if (equals(arr[i], res[j])) {
          isFind = true
          break
        }
      }
      if (!isFind) {
        res.push(arr[i])
      }
    }
    return res
  }

我们通过加一个自定义函数equals来判断内部对象内容是否相等,那我们现在来看aquals函数如何实现对对象内容是否相同的判断

function equals(v1, v2) {
    if ((typeof v1 === 'object' && v1 !== null) && (typeof v2 === 'object' && v2 !== null)) {
      if (Object.keys(v1).length !== Object.keys(v2).length) return false
  
      for (let key in v1) {
        if (key in v2) {
          // v1[key] === v2[key]
          if (!equals(v1[key], v2[key])) return false
        } else {
          return false
        }
      }
      return true  
    } else {
      return v1 === v2
    }
  }

我们先对函数equals传入两个对象v1 和 v2 我们先判断v1和v2类型是否为对象,如果是对象的话则判断他们里面的key值长度是否相同。如果key的数量是相同的,则继续遍历 v1和 v2 对象,判断内容是否相同,但我们知道对象内部仍有可能放入的内容仍为对象的,因此我们可以通过递归使用函数equals来实现。由此我们就大功已成。我们来看一下最终代码及运行测试

let arr = [
    {name: '张三', age: 18, like: {n: 1}},
    {name: '李四', age: 19},
    {name: '王五', age: 20},
    {name: '张三', age: 18, like: {n: 1}},
  ]
  
  // const s = new Set(arr)
  // console.log([...s]);
  
  function unique(arr) {
    let res = []
    
    for (let i = 0; i < arr.length; i++) {
      let isFind = false
      for (let j = 0; j < res.length; j++) {
        if (equals(arr[i], res[j])) {
          isFind = true
          break
        }
      }
      if (!isFind) {
        res.push(arr[i])
      }
    }
  
    return res
  }
  
  
  function equals(v1, v2) {
    if ((typeof v1 === 'object' && v1 !== null) && (typeof v2 === 'object' && v2 !== null)) {
      if (Object.keys(v1).length !== Object.keys(v2).length) return false
  
      for (let key in v1) {
        if (key in v2) {
          // v1[key] === v2[key]
          if (!equals(v1[key], v2[key])) return false
        } else {
          return false
        }
      }
  
      return true
  
    } else {
      return v1 === v2
    }
  }
  
  
  console.log(unique(arr));

image.png