数组去重
今天我们来讲数组去重的问题,我们这里有六种方法来对数组进行去重。每一个方法都可以互相穿插着用,这里只示例几种方法,这几种也已经够用了。我们先来简单的写一个去重方法:
1. 双层 for 循环
第一个方法:这也是数组去重的主思路。
我们定义一个新数组 newArr,for 循环遍历整个数组 arr ,把 arr 放入 newArr 中。
但是我们不能放入重复的值,我们来思考一下数组本身有没有方法,来查找新数组 newArr 里存不存在该值,要是想不到,我们可以写一个,用一个 for 循环来实现,若存在则不放入新数组 newArr。
最后 if 语句判断前面的查找是否查找完整个数组(查找完整个数组表示这个值不存在重复值),则可以 push 进新数组里。
const arr = [1, 2, 3, 4, 2, 1];
//去重
function unique(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
//新数组是否已经具有该值
for (var j = 0; j < newArr.length; j++) { //靠内层循环
if (arr[i] === newArr[j]) {
break; //破坏当前的循环语句
}
}
if(j === newArr.length) { //没有找到,说明没有重复元素
newArr.push(arr[i]);
}
}
return newArr;
}
unique(arr)
console.log(unique(arr));
看看结果,结果正确。这个时间复杂度是 n 的平方,空间复杂度是 n。
2. for + indexOf
3. for + includes
indexOf() 会返回 1 或 -1,includes() 方法会返回一个布尔值,true 或者 false。
这一个就是使用数组自身的查找方法:
const arr = [1, 2, 3, 4, 2, 1];
//去重
function unique(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1 ) { // 或者!newArr.includes(arr[i])
newArr.push(arr[i]);
}
}
return newArr;
}
unique(arr)
console.log(unique(arr));
这个时间复杂度和空间复杂度是多少呢?
没错,也是 n 的平方和 n。
4. filter + sort
我们换一个思路,先把数组排好序,如果第一个和第二个不一样,那肯定后面不存在一样的值,来看看数组自身的排序方法 sort() :
const arr = [1, 2, 3, 4, 2, 1];
arr.sort() //返回的是排序后的数组,不消耗空间
console.log(arr);
结果:输出了升序排列的数组。
那如果我们需要的是降序排列的数组呢?
我们可以使用箭头函数:
const arr = [1, 2, 3, 4, 2, 1];
arr.sort((a, b) => {
return b - a;
})
console.log(arr);
得到降序排列的数组:
这里要是想要简化代码,那我们把遍历数组这个 for 循环用数组自身的方法来替代,首先我们先来看看
arr.filter() 方法:
(从原始数组中筛选出满足特定条件的元素,并返回一个新的数组,把符合条件的放入新数组)
const arr = [1, 2, 3, 4, 2, 1];
arr.filter((item, index, arr) => { //值 下标 数组
console.log(item, index, arr);
})
这样我们就可以使用这个方法来遍历并筛选 item。
筛选数组:
const arr = [1, 2, 3, 4, 2, 1];
//遍历数组的方法
const arr2 = arr.filter((item, index, arr) => {
return item <= 2; //有一个空间复杂度,把满足条件的数组放到新数组中
})
console.log(arr2);
筛选出小于等于 2 的值并放入 arr2。
那我们现在来使用 sort() 和 filter() 方法来给数组去重:
const arr = [1, 2, 3, 4, 2, 1];
function unique(arr) { //cope一份原数组,不然以后拿去别的地方用,arr就一直是一个有序的数组
let newArr = [...arr] //浅拷贝:把 arr 的值展开到 newArr 中,所以 newArr 是一个新数组
return newArr.sort().filter((item, index, arr) => {
return index === 0 || item !== arr[index - 1];
//或者直接 return [...arr].sort().filter((item, index, arr) => !index || item !== arr[index - 1])
})
}
console.log(unique(arr));
来解释是一下这个代码:
这是返回一个新数组,这个数组是 newArr 数组 soft() 排序之后的,filter()筛选过后的新数组,筛选条件是:数组的第一个值 或者 数组的值不等于前一个值(表明这个值不重复)
return newArr.sort().filter((item, index, arr) => {
return index === 0 || item !== arr[index - 1];
})
结果正确,时间复杂度nlogn,空间复杂度n
5. filter + {}对象
这里来看看对象,给他两个相同的 key 值,会怎样?
let obj = { //对象不能存在重复的key
name: 'Joker',
name: 'Black',
age: 18,
city: '广州'
}
console.log(obj);
值会被覆盖
我们这里来模仿一下把数组放入对象:
let obj = {
1: 0,
2: 1,
3: 2,
2: 3,
1: 4
}
console.log(Object.keys(obj)); //对象这种数据结构的 key 只能是字符串
会输出字符串:
我们在这里利用对象的特性,来对数组进行去重。
let arr = [1, 2, 3, 2, 1]
function unique(arr) {
let obj = {}
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) { // 判断对象中是否有该key,我们可以通过读取对象的属性值是否存在来判断是否存在相同的值
obj[arr[i]] = true;
}
}
return Object.keys(obj).map(Number)
}
console.log(unique(arr));
时间复杂度 2n,空间复杂度也是 n。
我们这里利用对象的这个特性和 filter() 方法,来进行数组查重:
let arr = [1, 2, 3, 2, 1]
function unique(arr) {
let obj = {}
return arr.filter((item, index, arr) => {
return obj[item] ? false : (obj[item] = true)
})
}
console.log(unique(arr));
时间复杂度是 n,但是这里也损耗了空间,空间复杂度是 n 。
6. Set 新的数据结构
我们这里来学习一个新的引用类型,Set类型,也叫类数组:
let s = new Set(); //类数组,成员是唯一的
s.add(1) //存值
s.add(2)
//s.delete(1) //删除
//s.has(3) //判断 faluse
s.add(1) //重复值不存,
//无法取值
console.log(s);
可以看到结果没有重复值:
那我们也可以用 Set 类型来对数组进行去重操作:
const arr = [1, 2, 3, 4, 2, 1];
function unique(arr) {
return [...new Set(arr)] //Array.from(new Set(arr))
}
console.log(unique(arr));
时间复杂度 2n