剑指offer简单题型——找出数组中重复的数字,JavaScript的多种解题方法

1、面试题03:找出数组中重复的数字。

题目描述

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

限制:
2 <= n <= 100000

本题LeetCode链接:点击跳转

2、解题方法

使用JavaScript语言

方法一:使用ES6中Set对象

思路:
Set自动忽略数组中的重复元素

PS:Set的使用参考:JavaScript中Set对象的介绍(附示例)

代码一:新旧数组对比得到重复的数字
/**
    * @param {number[]} nums
    * @return {number}
*/
/*  
   数组会溢出
   const findRepeatNumber = function (nums) {
      let newNums = [...new Set(nums)];
      // let newNums = Array.from(new Set(nums));
      for (let i = 0; i < nums.length; i++) {
      	 if (nums[i] !== newNums[i]) {
      		 return nums[i];
         }
      }
   }
*/
// 一开始什么都没考虑就直接用了原数组的长度,后来反应过来 新数组的长度比原数组的短,
//所以用原数组的长度很有可能溢出。比如nums = [2, 3, 1, 0, 5, 3];

//下面的代码是使用新数组的长度,如果循环完还没有找到,并且原数组长度 > 新数组长度,
// 则原数组里没有遍历到的第一个数字就是重复的数字
        var findRepeatNumber = function (nums) {
            let newNums = [...new Set(nums)];
            let len = newNums.length;
            if (nums.length == len) return null;
            // 这个也可以不做判断,因为题目说了数组中是存在有重复的数字的。
            for (let i = 0; i < len; i++) {
                if (nums[i] !== newNums[i]) {
                    return nums[i]
                }
            }
            return nums[len];
        }
代码二:利用Set的size属性

遍历数组中元素,放入Set对象中。若Set对象的长度未增加,则输出当前元素。

      var findRepeatNumber = function (nums) {
            let s = new Set();             //set为自动忽略重复元素
            for (let i in nums) {
                let current = s.size;      //记录当前set的长度
                s.add(nums[i]);            //遍历数组,将元素添加到s中
                if (s.size == current) {   
                //如果有重复的元素添加时,会自动被忽略掉,即s的长度不变
                    return nums[i];
                }
            }
        };

        //也可以把重复的放在一个数组里,可取到不同的重复数字
        var findRepeatNumber = function (nums) {
            let s = new Set();             //set为自动忽略重复元素
            let j = 0, result = [];        //定义一个数组来存放重复的数字
            for (let i in nums) {
                let current = s.size;      //记录当前set的长度
                s.add(nums[i]);            //遍历数组,将元素添加到s中
                if (s.size == current) {  
                    result[j] = nums[i];
                    j++;
                }
            }
            return result[0];
        };
代码三:利用Set的has方法

遍历数组,判断Set里面是否存在该元素,存在则返回该元素;不存在则把该元素添加到Set中
(也可以新建一个数组,使用数组的includes方法,下面会提到)

var findRepeatNumber = function (nums) {
            let s = new Set();
            let len = nums.length;
            for (let i = 0; i < len; i++) {
                if (s.has(nums[i])) {        //如果s存在该元素则返回
                    return nums[i];
                }
                else {
                    s.add(nums[i]);          //如果不存在该元素则添加到s
                }
            }
        };

方法二:利用obj对象的键值对形式

将数组中的元素变成对象的key,出现的次数变成value

代码一:把数组的元素和出现次数存入对象中
        var findRepeatNumber = function (nums) {
            let obj = {};
            let res;
            for (let num of nums) {    // for...in 和 for...of 的区别
                if (obj[num]) {
                    obj[num] = obj[num] + 1; //js对象属性obj.attr 和 obj[attr]的区别
                    return num;
                } else {
                    obj[num] = 1;
                }
            }
        };   

顺手补上一个链接:js 对象属性通过点(.) 和 方括号 ( [] ) 的不同之处:obj.attr 和 obj[attr]

这里补上力扣上的一个热门答案:使用哈希表和原地哈希

代码二:循环也可以用数组的find方法代替

find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。(ES6数组的新方法)
find() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 undefined
注意: find() 对于空数组,函数是不会执行的。
注意: find() 并没有改变数组的原始值。

var findRepeatNumber = function (nums) {
            let obj = {};
            let result;
            nums.find(num => {   //num是数组里面的每一个元素
                if (obj[num]) {
                    result= num;
                    return num;
                }
                obj[num] = 1;
            })
            return result;
        };

方法三:使用ES6的Map对象

跟Set方法的代码三很相似,其思路又很像把数组变成obj对象的键值对。

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
Map对象的具体使用方法以及与Obj对象的区别可自行百度

var findRepeatNumber = function (nums) {
            let len = nums.length;
            let map = new Map();
            for (let i = 0; i < len; i++) {
                if (map.has(nums[i]))
                    return nums[i];
                map.set(nums[i], true);
            }
        };

方法四:判断数组的下标

代码一:利用js数组方法indexOf和lastIndexOf,从前后两个顺序遍历,发现下标不同,则是有重复的

indexOf() 方法可返回数组中某个指定的元素位置。
该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。开始位置的索引为 0。

lastIndexOf() 方法可返回一个指定的元素在数组中最后出现的位置,从该字符串的后面向前查找。
该方法将从尾到头地检索数组中指定元素 item。开始检索的位置在数组的 start 处或数组的结尾(没有指定 start 参数时)。如果找到一个 item,则返回 item 从尾向前检索第一个次出现在数组的位置。数组的索引开始位置是从 0 开始的

indexOf() 和 lastIndexOf() 如果要检索的元素没有出现,则该方法返回 -1。

var findRepeatNumber = function (nums) {
            for (let i = 0; i < nums.length; i++) {
                let result = nums[i];
                // if (nums.indexOf(result) != nums.lastIndexOf(result)) {
                if (i != nums.lastIndexOf(result)) {
                    return result;
                }
            }
            return null;
        }
代码二:新建数组,判断新数组是否存在重复的数字

(跟之前的Set方法,新建一个数组有相似的地方)

var findRepeatNumber = function (nums) {
            let arr = [];
            for (let i in nums) {
                if(arr.includes(nums[i])){
                // if(arr.indexOf(nums[i]) > -1){  //使用includes方法或者indexOf方法
                    return nums[i];
                }
                arr.push(nums[i]);
            }
        };
代码三:遍历数组,判断这个数字在数组的剩余内容中是否再次出现
var findRepeatNumber = function (nums) {
            let len = nums.length;
            for (let i = 0; i < len; i++) {
                let result = nums[0];
                // nums.splice(0, 1);
                nums.shift();
                if(nums.indexOf(result) > -1){
                    return result;
                }
            }
        };
        
/*  错误代码
        var findRepeatNumber = function (nums) {
            for (let i in nums) {
                let result = nums[i];
                nums.splice(i, 1);
                if(nums.indexOf(result) > -1){
                    return result;
                }
            }
        };
        */

方法五:直接判断该数值是否重复

代码一:排序之后比较相邻的数字
var findRepeatNumber = function (nums) {
            nums.sort((a, b) => a - b);
            let len = nums.length - 1;    //因为数组会溢出,所以要减一
            for (let i = 0; i < len; i++) {
                if (nums[i] === nums[i + 1]) return nums[i];
            }
            return -1;
        }
代码二:双循环逐一比较数值
var findRepeatNumber = function (nums) {
            let len = nums.length;
            for (let i = 0; i < len; i++) {
                for (let j = i + 1; j < len; j++) {
                    if (nums[i] === nums[j]) {
                        return nums[i];
                    }
                }
            }
        }

3、总结

方法一:使用ES6的Set对象(size属性、has方法);
方法二:使用ES6的Map对象;
方法三:使用obj对象,把数组中的数字和出现次数存为键值对形式;
方法四:比较数字的下标,使用一些数组方法(lastIndexOf、indexOf、includes等);
方法五:直接比较数字,使用排序后比较相邻数组、双循环比较数字。

大概的时间复杂度排序:方法一二三 < 方法五的排序后比较 < 方法四、方法五双循环

js数组方法是有一定的时间复杂度的,具体参考JS - 数组方法的效率

——————————————————分割线————————————————

①以上代码部分来自于leetcode的代码解答,这里做一个汇总整理,方便自己记忆。
②牛客网上有很多面经,也有很多笔试题,对找工作非常有帮助。但是编程题上没办法只查看针对某一语言的代码解析(这里不是说代码,而是指评论解答)。leetcode的编程题非常对,也非常友好(可以只看某某一语言的解答,可以直接搜索某种解题方法)。牛客网的评论量很大,大神后面会有很多的留言什么的。leetcode上面除了大神的解答,好像就,没了。
③初次写博客,如有错误欢迎指出来。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值