js数组去重算法总结

一、去重

我在前端面试过程中遇到去重算法的概率为百分之九十,这里就总结下各种去重算法及其复杂度

1. new Set()

ES6中介绍了Set相关的概念,它可以实现数组和字符串去重的功能,代码量非常少,一行代码的事情

//数组去重
const arr = [1,1,2,2]
function unique1(arr){
  return [...new Set(arr)]  //[1,2]
}
//或者
function unique1(arr){
   return Array.from(new Set(arr))  //[1,2]
}
//补充 字符串去重
[...new Set('aabbccs')].join('') //'abcs'

2. filter + indexOf 过滤

indexOf有兼容性问题,支持ie8以上

这里我就假设传进来的一定是一个合法的数组,那些对入参的校验这里就不作处理了,直接写逻辑代码。

function unique2(arr){
   return arr.filter((item,index,arr)=>arr.indexOf(item) == index)
}
console.log(fn([1,1,2,2,3]))//[1,2,3]

这个方法利用了filter的第三个参数还是返回原数组,通过判断这个数组的第n个元素的位置是不是原始位置来过滤重复数字。例如,第一个1出现在0位置上,那么arr.indexOf(item) = 0 和index = 0 是匹配上的,第2个1出现在1位置上但是arr.indexOf(item) 还是等于0,而此时的index = 1 那么这时候 arr.indexOf(item) 不等于 index,这样重复的number就被过滤掉啦

3.object 对象属性不重复

该方法速度最快,以空间换时间。不过需要注意的是,该方法中判断数组元素是否为对象的键值

function unique3(arr){
    const obj = {}
    arr.map(item=> obj[item] = item)
    return Object.keys(obj)
}
console.log(unique3([1,1,2,2,3])) //['1','2','3']

注意:这个方法返回的值为字符串了,这里注意要转换下

或者是创建个对象和新数组

    function unique3(arr){
      const obj = {}
      let arr = []
      arr.forEach(item=>{
         if(!obj[item]){
            obj[item] = 1
            arr.push(item)
         }else { // else可要可不要,要的话可以记录下每个元素重复的次数
            obj[item] += 1
          }
      })
      return arr
   }

只创建一个对象,遇到重复元素就删除

function unique3(arr){
    const obj = {}
    for(let i =0;i<arr.length;i++){ //要动态改变length所以只能用for循环
      const item = arr[i]
       if(obj[item]){  //存在重复就删除,且i--
           obj[item] += 1
           arr.splice(i,1) //
           i--
       }else {
         obj[item] = 1
       }
    }
   return arr
}

4. indexOf + 新数组

新增一个数组,判断这个新数组中是否存在item元素,不存在就push,存在就忽略

function unique4(arr){
   const newArr = []
   arr.forEach(item=>{
       newArr.indexOf(item) === -1 ? newArr.push(item):null
   })
   return newArr
}
console.log(unique4([1,1,2,2,3])) //[1,2,3]

5. includes + 新数组

思路和方法4一样,只是使用方法由indexOf改为includes

function unique5(arr){
   const newArr = []
   arr.forEach(item=>{
       !newArr.includes(item)  ? newArr.push(item):null
   })
   return newArr
}
console.log(unique5([1,1,2,2,3])) //[1,2,3]

6.对象数组中id重复的去重

const repeatArr = [
  { id: 1, a: 1 },
  { id: 2, a: 2 },
  { id: 3, a: 3 },
  { id: 1, a: 4 },
];
const newArr = repeatArr.reduce((arr, cur) => {
    const ids = arr.map(item => item.id);
    return ids.includes(cur.id) ? arr : [...arr, cur];
}, []);
console.log(newArr); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]

二、将mxn矩阵的数组转换为nxm的数组

将数组中每个元素中的第一位组成新数组中第一个元素,数组中每个元素的第二位组成新数组中的第二个元素.....直到最后一位
题目描述可能有点绕,具体可以看看示例就能明白了
例如
示例一:arr = [ [1, 2, 3 ], [4, 5, 6 ],[7, 8, 9 ] ],将arr转换为 arr = [[1,4,7], [2,5,8],[3,6,9]]
示例二:arr = [ [1, 2, 3,4 ], [5, 6,7,8 ],[9,10,11,12 ] ],将arr转换为 arr = [[1,5,9], [2,6,10],[3,7,11],[4,8,12]]
参考解答方案

 let arr = [
            [1,  2,  3,  4],
            [5,  6,  7,  8],
            [9, 10, 11, 12],
            [13,14, 15, 16],
            [17,18, 19, 20],
        ]
function coverFun(arr) {
   //这里先定义一个新的空的数组,将新数组个长度先定义下
   //两种方法都可以
   let newArr = Array.apply(null,{length:arr[0].length})
                        .map((item,i)=>{return []})
    // let newArr = Array.from({length:arr[0].length})
    //                    .map((item,i)=>{return []})
    //外层遍历数组第一个元素的长度(这里随便哪个元素都行,因为都是一样的)
    //里层遍历数组长度
    for (let i = 0; i < arr[0].length; i++) {
         for (let j = 0; j < arr.length; j++) {
              newArr[i].push(arr[j][i])
          }
    }
    return newArr
}
console.log(coverFun(arr))

最后输出结果如图


输出结果

当时一紧张脑子啥也没想到,回来好好看了算法相关的题目,将js相关的算法汇总下。
当然上面的只是我想到的其中一种解题思路,肯定会有很多其他办法也可以实现的。

三、螺旋矩阵

给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

例如输入
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

这题跟上面的点相似之处

四、判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:
输入: 121
输出: true

示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
参考解答方案

var isPalindrome = function(x) {
    if((x+'').indexOf('-') >-1){
        return false
    }
    let str = String(x)
    str = str.split('').reverse().join('')
    if(str === String(x) ){
        return true
    }else {
        return false
    }
};

五、盛最多水的容器

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。


图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49
参考解答方案

var maxArea = function(height) {
    let max = 0//假设最大为0
    for(let i= 0;i<=height.length-1;i++){
        for(let j=1+i;j<=height.length-1;j++){
            if(Math.min(height[i],height[j])*Math.abs(i-j)>max)
               max =  Math.min(height[i],height[j])*Math.abs(i-j)
        }
    }
    return max
};

六、最大公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""。

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"
示例 2:

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:

所有输入只包含小写字母 a-z 。
参考解答方案

var longestCommonPrefix = function(strs) {
    if(strs.length ===0){
        return ''
    }else if(strs.length ===1){
        return strs[0]
    }
    let index = ''//假设index是公共字符串
     for(let i = 0;i<strs[0].length;i++){
         let comStr = strs[0].charAt(i)
        for(let j = 1; j<strs.length;j++){
            if(strs[j].charAt(i)!==comStr){
                return index
            }
        }
        index += comStr
    }
    return index
};

七、三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

参考解答方案

var threeSum = function(nums) {
    let arr = []
     for(let i=0;i<nums.length;i++){
         for(let j = 1;j<nums.length;j++){
             for(let c=2;c<nums.length;c++){
                 if((nums[i]+nums[j]) === -nums[c]&& i!==j &&i!==c&&j!==c){
                     arr.push([nums[i],nums[j],nums[c]])
                 }
             }
         }
     }
    let a = arr.map(item=>{
        return item.sort().join(',')
    })
    let lastArr = [...new Set(a)]
    
    return  lastArr.map(item=>{
          return item.split(',').map(item=>{
              return Number(item)
          })
    })
};

八、最大子序和

描述

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

九、最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

十、爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

例如:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶
var climbStairs = function(n) {
    let arr = [],index=0
  //1.首先先算出有多少1和2的组合
    for( let i = 0; i <= n; i++ ){
        for( let j = 0; j<=n;j++){
            if(i*1 + 2*j === n){  //i 个1, j个2
                arr.push([i,j]) //
            }
        }
    }
    //这是一个实现乘积的递归算法
    function getCJ(n){
        if(n=1){
            return 1
        }
        return n*getCJ(n-1)
    }
   //2.在计算每一种组合内部有多少排列方式
    for (let n =0;n<arr.length;n++){
        let item = arr[n]
       //2.1当组合中都是1或者都是2时候,只有1种排列方式
        if(item.indexOf(0)>-1){
            index +=1
      //2.1当组合中的1或者2只有1个的时候排列方式为 当前数组元素相加的和
        }else if((item.indexOf(1)>-1)){
            index += item[0] +item[1]
        }else{
           //2.3最复杂的情况 m个1和n个2的情况(m,n>=2) 排列方式 有(m+n)!/(m!*n!)种
            index += getCJ(item[0]+item[1])/(getCJ(item[0])*getCJ(item[1]))
        }
    }
    return index
};
console.log(climbStairs(7)) //21
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 算法思想贪心思想双指针排序快速选择堆排序桶排序荷兰国旗问题二分查找搜索BFSDFSBacktracking分治动态...
    第六象限阅读 2,983评论 0 0
  • 这几天刷屏都是大龄熟女的鲜嫩爱情亮瞎了单身剩女的眼睛,于是有人激动热泪盈眶地欢呼“她时代来了!”有人则撇着嘴说:"...
    君子既来云胡不喜阅读 210评论 0 0
  • 喧喧暑气四加一, 修补空调未忘期。 如盼星星如盼月, 七天依旧汗漓漓。
    迷蝴庄生阅读 263评论 2 5
  • 伊河岸踏春 寻春觅春不见春,半枝新芽黄绿孕。 春风春雨春日处,伊河伊水伊川人。
    拣呗阅读 325评论 0 8
  • 简陋的天花板,一张床,发出微光的台灯,两支显眼的针管躺在地板,一具赤裸尸体。慌乱的人群,警车发出刺耳声音。 从旅馆...
    斑斑96阅读 654评论 3 6