JavaScript数组去重

对一个长度为n的数组arr去重得到数组res,大概有三种方法:哈希 (hash)、排序和遍历。

遍历

遍历是暴力方法,也是最慢的方法。即对每个输入数组arr中的元素arr[i],都在结果数组res中进行查找——若不存在,则将当前值arr[i]加入结果数组res;若已存在,则不再加入。

遍历实现数组去重

function deduplicateByBF(input) {
    let output = [];
    for (item of input) {
        if (output.indexOf(item) === -1) {
            output.push(item);
        }
    }
    return output;
}

let arr = [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43];
console.log(`Input:`, arr);

let res = deduplicateByBF(arr);
console.log(`Output:`, res);

//Input: [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43]
//Output: [5, 77, 3, 43, 74, 2]

时间复杂度:T(n)=O(n²)
空间复杂度:S(n)=O(1)

排序

我们先对输入数组arr排序,之后遍历一次输入数组arr

由于已经排序,重复值一定相邻,所以在每一个位置i只需要判断当前值arr[i]是否等于前一个值arr[i-1]即可。
如果不等,则当前值为第一次出现,记录结果;如果相等,则当前值已经被记录,不再重复记录。

排序实现数组去重

function deduplicateBySorting(input) {
    let output = [];
    input.sort((a, b) => a - b);
    if (input.length > 0) {
        output.push(input[0]);
    }
    for (let i = 1; i < input.length; i++) {
        if (input[i] !== input[i - 1]) {
            output.push(input[i]);
        }
    }
    return output;
}

let arr = [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43];
console.log(`Input:`, arr);

let res = deduplicateBySorting(arr);
console.log(`Output:`, res);

//Input: [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43]
//Output: [2, 3, 5, 43, 74, 77]

时间复杂度:T(n)=O(n·logn)
空间复杂度:S(n)=O(1)

使用排序的注意事项

以上例子使用的为全数字数组。如果有其他数据类型(如对象),仍然可以使用,但是需要加以改进。

比如在比较大小时不再使用(a, b) => a - b,而是先将二者序列化,再按字符串字典序比较二者大小。但这种改进可能严重拖慢程序运行速度,应当避免。

所以,只有在确保输入数据为全数值数组的情况下才使用排序去重,否则请使用下文的hash去重。

Hash

Hash 简介

Hash(哈希)是用散列函数将目标对象转化为有代表性的特征值,这个特征值起到数组下标的作用,可以将目标对象映射到一个内存空间。

散列函数一般运算很快,所以通过hash来读写目标对象非常迅速,不需要如“遍历”等高耗时操作。通过设计合适的散列函数,hash结构的索引平均时间复杂度为O(1)
相比于循环遍历数组结构,使用hash可以节省时间开销,但会增加空间开销(用于保存hash table),属于“用空间换时间”。

Set、map都是常见的hash结构。Set只记录某个元素是否被加入,map则同时要记录元素对应的值(即保存键值对)。
JavaScript中,对象的键值对本质上就是一种map,也是通过hash实现的。

Hash 实现数组去重

function deduplicateByHashSet(input) {
    let output = [];
    let used = new Set();
    for (item of input) {
        if (!used.has(item)) {
            used.add(item);
            output.push(item);
        }
    }
    return output;
}

let arr = [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43];
console.log(`Input:`, arr);

let res = deduplicateByHashSet(arr);
console.log(`Output:`, res);

//Input: [5, 77, 3, 43, 74, 2, 5, 3, 77, 2, 74, 43]
//Output: [5, 77, 3, 43, 74, 2]

时间复杂度:T(n)=O(n)
空间复杂度:S(n)=O(n)

后记

很多人对数组去重这个简单问题洋洋洒洒列举出了七八种解决方法,但这些方法不过是上述暴力遍历和hash的修改:例如把Set改为Map或者Object的属性,把调用indexOf方法改为用手写for循环实现。

这些修改在我看来没有意义,很多文章甚至没有想到排序的方法——这些乱象催生了这篇文章。

希望对你有所帮助。

你可能感兴趣的:(javascript,前端,算法)