面试题:数组去重的N种方法

667 阅读2分钟

这是我参与更文挑战的第23天,活动详情查看: 更文挑战

前提

面试官在考察面试者的数组基础的时候,经常会问面试者一道题:

数组该如何去重?

这道题比较高频,因为它可以考察面试者对数组方法掌握的熟悉程度,以及思考能力和算法能力。今天咱们来看看,看一下能罗列出多少种方法。

数组去重

问题: 数组如何去重?

例子: [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]

期望: [1, 2, 3, 5, 6, 4]

这里数组的元素主要是针对基本类型,引用类型暂不考虑。

双重遍历法

第一种实现

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
for (let i = 0; i < arr.length; i++) {
  for (let j = i + 1; j < arr.length; j++) {
    if (arr[i] === arr[j]) {
      arr.splice(j, 1)
      // 因为splice删除后 后面元素往前移动了,j要减一
      j--
    }
  }
}
console.log(arr) // [1, 2, 3, 5, 6, 4] 

原理:

第一重遍历数组,在这个遍历里面,对数组再遍历,下标从i+1开始(因为如果重复只能是在i之后的元素);
如果在第二重遍历中发现arr[i] === arr[j],则把该元素从j处删除(也可以从i处删除,不推荐,因为顺序会变),这里要注意的是arr删除后,j也要减一(因为arr删除后,后面的元素往前移动了一步)

第二种实现

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let isDuplicate = false
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
  isDuplicate = false
  for (let j = 0; j < uniqueArr.length; j++) {
    if (arr[i] === uniqueArr[j]) {
      isDuplicate = true
    }
  }
  // 没有重复,则把元素push到uniqueArr中
  !isDuplicate && uniqueArr.push(arr[i])
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4] 

原理:

设置一个标志位isDuplicate, 让源数组跟去重后的数组的元素对比, 如果有相等的,则证明是重复了,标志位为true, 不做任何处理。如果没有,则标志位是false, 则把该元素push到去重后的数组中。

indexOf函数

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
  if (arr.indexOf(arr[i]) === i) {
    uniqueArr.push(arr[i])
  }
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4] 

原理:

主要是利用indexOf函数,indexOf函数返回的下标和遍历的下标i,如果是相等的,则证明是没有重复的,然后pushuniqueArr中,不相等则代表之前出现过了,是重复的,则不处理。

include函数

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
  if (!uniqueArr.includes(arr[i])) {
    uniqueArr.push(arr[i])
  }
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4] 

原理:

主要是利用include函数,include函数返回布尔值,代表数组是否已经存在该元素,如果不存在则pushuniqueArr中,存在则代表uniqueArr数组已经存在了,无需再push了。

Set函数

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr= [...new Set(arr)]
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4] 

原理:

主要是利用es6的Set函数,它的内部元素都是唯一的,没有重复值,最后用spread操作符把它变成数组。

Map函数

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
let map = new Map()
for (let i = 0; i < arr.length; i++) {
  if (!map.get(arr[i])) {
    map.set(arr[i], true)
    uniqueArr.push(arr[i])
  }
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]

原理:

主要是利用es6的Map函数,可以用对象当键的键值对集合,如果Map存在元素,则返回true,则代表uniqueArr数组已经存在该元素了,无需处理。

对象的hasOwnProperty

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
let obj = {}
for (let i = 0; i < arr.length; i++) {
  if (!obj.hasOwnProperty(typeof arr[i] + arr[i])) {
    obj[typeof arr[i] + arr[i]] = true
    uniqueArr.push(arr[i])
  }
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]

原理:

主要是利用对象的hasOwnProperty函数,如果对象存在有待检验的key,那么hasOwnProperty(key)就返回true,否则返回false。把数组的元素存进对象中,hasOwnProperty(元素)如果返回true, 则证明uniqueArr已经存在该元素了,无需再push了。

这里是用typeof arr[i] + arr[i]key,主要是为了兼容数组可能出现字符串'1'和数字1被当作同一种值去重了。

sort排序

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
arr.sort() 
for (let i = 0; i < arr.length; i++) {
  if(arr[i] !== arr[i + 1]) {
    uniqueArr.push(arr[i])
  }
}
console.log(uniqueArr) // [1, 2, 3, 4, 5, 6]

原理:

主要是利用sort函数进行排序,排序后然后对数组遍历,如果相邻元素不相等,则证明uniqueArr还没有该元素,然后就可以push进去。否则不处理。

reduce函数

let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = arr.sort().reduce((acc, cur, i) => {
  if (acc.length === 0 || acc[acc.length - 1] !== cur) {
    acc.push(cur)
  }
  return acc
}, [])

console.log(uniqueArr) // [1, 2, 3, 4, 5, 6]

原理:

主要是利用reduce函数,先排序,然后传入一个新数组,然后判断如果是新数组的length等于0 或者新数组的元素和遍历元素对比,如果不等就则push元素。

总结

整体的思路主要是这些:

  • 双重遍历数组,发现相等的就剔除,接着遍历
  • 新建一个新的空数组,遍历数组,如果空数组不存在该元素,则push到空数组中
  • 利用对象/Map的key的不重复特性
  • 利用Set的元素的唯一性
  • 排序数组,然后遍历数组,左右相互对比,不相等则push到空数组中

推荐:

对象/MapSet的方案好一些,复杂度不高,可以对NaN去重,其它的方案复杂度会高一些。

以上就是我总结的数组去重的N种方法,大家可以都动手敲敲试试,理解了原理就不怕面试官问啦~